1 /* GtkSheet widget for Gtk+.
2 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
3 *
4 * Based on GtkClist widget by Jay Painter, but major changes.
5 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23 /**
24 * SECTION: gtksheet
25 * @short_description: A spreadsheet widget for Gtk+
26 *
27 * #GtkSheet is a matrix widget for GTK+. It consists of an scrollable grid of
28 * cells where you can put text or other #GtkWidget's in. Cells
29 * are organized in rows (#GtkSheetRow) and columns
30 * (#GtkSheetColumn). Cell contents can be edited interactively
31 * through a specially designed entry (#GtkItemEntry). A
32 * #GtkSheet is also a container subclass, allowing you to
33 * display buttons, curves, pixmaps and any other widget in it.
34 * You can also set many attributes as: border, foreground and
35 * background color, text justification, and more. The
36 * testgtksheet program shows how easy is to create a
37 * spreadsheet-like GUI using this widget set.
38 */
39
40
41 #include <string.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44
45 #include <glib.h>
46 #include <gdk/gdk.h>
47 #include <gtk/gtk.h>
48 #include <gdk/gdkkeysyms.h>
49 #include <gtk/gtksignal.h>
50 #include <gtk/gtkpixmap.h>
51 #include <pango/pango.h>
52
53 #define __GTKEXTRA_H_INSIDE__
54
55 #include "gtkextra-compat.h"
56 #include "gtkitementry.h"
57 #include "gtkdatatextview.h"
58 #include "gtksheet.h"
59 #include "gtkdataformat.h"
60 #include "gtkextra-marshal.h"
61 #include "gtkextratypebuiltins.h"
62
63 #undef GTK_SHEET_DEBUG
64
65 #ifdef DEBUG
66 # undef GTK_SHEET_DEBUG
67 #define GTK_SHEET_DEBUG 0 /* define to activate debug output */
68 #endif
69
70 #ifdef GTK_SHEET_DEBUG
71 # define GTK_SHEET_DEBUG_ADJUSTMENT 0
72 # define GTK_SHEET_DEBUG_ALLOCATION 0
73 # define GTK_SHEET_DEBUG_BUILDER 0
74 # define GTK_SHEET_DEBUG_CELL_ACTIVATION 0
75 # define GTK_SHEET_DEBUG_CHILDREN 0
76 # define GTK_SHEET_DEBUG_CLICK 0
77 # define GTK_SHEET_DEBUG_COLORS 0
78 # define GTK_SHEET_DEBUG_DRAW 0
79 # define GTK_SHEET_DEBUG_DRAW_BACKGROUND 0
80 # define GTK_SHEET_DEBUG_DRAW_BUTTON 0
81 # define GTK_SHEET_DEBUG_DRAW_LABEL 0
82 # define GTK_SHEET_DEBUG_ENTER_PRESSED 0
83 # define GTK_SHEET_DEBUG_ENTRY 0
84 # define GTK_SHEET_DEBUG_EXPOSE 0
85 # define GTK_SHEET_DEBUG_FINALIZE 0
86 # define GTK_SHEET_DEBUG_FONT_METRICS 0
87 # define GTK_SHEET_DEBUG_FREEZE 0
88 # define GTK_SHEET_DEBUG_KEYPRESS 0
89 # define GTK_SHEET_DEBUG_MOUSE 0
90 # define GTK_SHEET_DEBUG_MOVE 0
91 # define GTK_SHEET_DEBUG_MOTION 0
92 # define GTK_SHEET_DEBUG_PIXEL_INFO 0
93 # define GTK_SHEET_DEBUG_PROPERTIES 0
94 # define GTK_SHEET_DEBUG_REALIZE 0
95 # define GTK_SHEET_DEBUG_SELECTION 0
96 # define GTK_SHEET_DEBUG_SIGNALS 0
97 # define GTK_SHEET_DEBUG_SIZE 0
98 # define GTK_SHEET_DEBUG_SET_CELL_TIMER 0
99 # define GTK_SHEET_DEBUG_SET_CELL_TEXT 0
100 # define GTK_SHEET_DEBUG_SET_ENTRY_TEXT 0
101 #endif
102
103 #define GTK_SHEET_MOD_MASK GDK_MOD1_MASK /* main modifier for sheet navigation */
104
105 #ifndef GDK_KEY_KP_Up
106 # define GDK_KEY_KP_Up GDK_KP_Up
107 # define GDK_KEY_KP_Down GDK_KP_Down
108 # define GDK_KEY_KP_Page_Up GDK_KP_Page_Up
109 # define GDK_KEY_KP_Page_Down GDK_KP_Page_Down
110 # define GDK_KEY_KP_Page_Left GDK_KP_Page_Left
111 # define GDK_KEY_KP_Page_Right GDK_KP_Page_Right
112 # define GDK_KEY_KP_Home GDK_KP_Home
113 # define GDK_KEY_KP_End GDK_KP_End
114 # define GDK_KEY_KP_Left GDK_KP_Left
115 # define GDK_KEY_KP_Right GDK_KP_Right
116 # define GDK_KEY_KP_Enter GDK_KP_Enter
117 #endif
118
119 #if !GTK_CHECK_VERSION(2,22,0)
120 static GdkCursorType
gdk_cursor_get_cursor_type(GdkCursor * cursor)121 gdk_cursor_get_cursor_type (GdkCursor *cursor)
122 {
123 g_return_val_if_fail (cursor != NULL, GDK_BLANK_CURSOR);
124 return cursor->type;
125 }
126 #endif
127 /* sheet flags */
128 enum _GtkSheetFlags
129 {
130 GTK_SHEET_IS_LOCKED = 1 << 0, /* sheet is not editable */
131 GTK_SHEET_IS_FROZEN = 1 << 1, /* frontend updates temporarily disabled */
132 GTK_SHEET_IN_XDRAG = 1 << 2, /* column being resized */
133 GTK_SHEET_IN_YDRAG = 1 << 3, /* row being resized */
134 GTK_SHEET_IN_DRAG = 1 << 4, /* cell selection being moved */
135 GTK_SHEET_IN_SELECTION = 1 << 5, /* cell selection being created */
136 GTK_SHEET_IN_RESIZE = 1 << 6, /* cell selection being resized */
137 GTK_SHEET_IN_CLIP = 1 << 7, /* cell selection in clipboard */
138 GTK_SHEET_IN_REDRAW_PENDING = 1 << 8, /* redraw on deactivate */
139 GTK_SHEET_IN_AUTORESIZE_PENDING = 1 << 9, /* autoresize pending */
140 GTK_SHEET_IS_DESTROYED = 1 << 10, /* set by destruct handler */
141 };
142
143 enum _GtkSheetProperties
144 {
145 PROP_GTK_SHEET_0, /* dummy */
146 PROP_GTK_SHEET_TITLE, /* gtk_sheet_set_title() */
147 PROP_GTK_SHEET_DESCRIPTION, /* gtk_sheet_set_description() */
148 PROP_GTK_SHEET_NCOLS, /* number of colunms - necessary for glade object creation */
149 PROP_GTK_SHEET_NROWS, /* number of rows - necessary for glade object creation */
150 PROP_GTK_SHEET_LOCKED, /* gtk_sheet_set_locked() */
151 PROP_GTK_SHEET_SELECTION_MODE, /* gtk_sheet_set_selection_mode() */
152 PROP_GTK_SHEET_AUTO_RESIZE, /* gtk_sheet_set_autoresize() */
153 PROP_GTK_SHEET_AUTO_RESIZE_ROWS, /* gtk_sheet_set_autoresize_rows() */
154 PROP_GTK_SHEET_AUTO_RESIZE_COLUMNS, /* gtk_sheet_set_autoresize_columns() */
155 PROP_GTK_SHEET_AUTO_SCROLL, /* gtk_sheet_set_autoscroll() */
156 PROP_GTK_SHEET_CLIP_TEXT, /* gtk_sheet_set_clip_text() */
157 PROP_GTK_SHEET_JUSTIFY_ENTRY, /* gtk_sheet_set_justify_entry() */
158 PROP_GTK_SHEET_BG_COLOR, /* gtk_sheet_set_background() */
159 PROP_GTK_SHEET_GRID_VISIBLE, /* gtk_sheet_show_grid() */
160 PROP_GTK_SHEET_GRID_COLOR, /* gtk_sheet_set_grid() */
161 PROP_GTK_SHEET_COLUMN_TITLES_VISIBLE, /* gtk_sheet_show_column_titles() */
162 PROP_GTK_SHEET_COLUMNS_RESIZABLE, /* gtk_sheet_columns_set_resizable() */
163 PROP_GTK_SHEET_COLUMN_TITLES_HEIGHT, /* gtk_sheet_set_column_titles_height() */
164 PROP_GTK_SHEET_ROW_TITLES_VISIBLE, /* gtk_sheet_show_row_titles() */
165 PROP_GTK_SHEET_ROWS_RESIZABLE, /* gtk_sheet_rows_set_resizable() */
166 PROP_GTK_SHEET_ROW_TITLES_WIDTH, /* gtk_sheet_set_row_titles_width() */
167 PROP_GTK_SHEET_ENTRY_TYPE, /* gtk_sheet_change_entry() */
168 PROP_GTK_SHEET_VJUST, /* gtk_sheet_set_vjustification() */
169 PROP_GTK_SHEET_TRAVERSE_TYPE, /* gtk_sheet_set_traverse_type() */
170 };
171
172 /* Signals */
173
174 enum _GtkSheetSignals
175 {
176 SELECT_ROW,
177 SELECT_COLUMN,
178 SELECT_RANGE,
179 CLIP_RANGE,
180 RESIZE_RANGE,
181 MOVE_RANGE,
182 TRAVERSE,
183 DEACTIVATE,
184 ACTIVATE,
185 SET_CELL,
186 CLEAR_CELL,
187 CHANGED,
188 NEW_COL_WIDTH,
189 NEW_ROW_HEIGHT,
190 ENTRY_FOCUS_IN,
191 ENTRY_FOCUS_OUT,
192 ENTRY_POPULATE_POPUP,
193 MOVE_CURSOR,
194 ENTER_PRESSED,
195 LAST_SIGNAL
196 };
197 static guint sheet_signals[LAST_SIGNAL] = { 0 };
198
199 typedef enum _GtkSheetArea
200 {
201 ON_SHEET_BUTTON_AREA,
202 ON_ROW_TITLES_AREA,
203 ON_COLUMN_TITLES_AREA,
204 ON_CELL_AREA
205 } GtkSheetArea;
206
207 #define GTK_SHEET_FLAGS(sheet) (GTK_SHEET (sheet)->flags)
208 #define GTK_SHEET_SET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) |= (flag))
209 #define GTK_SHEET_UNSET_FLAGS(sheet,flag) (GTK_SHEET_FLAGS (sheet) &= ~(flag))
210
211 #define GTK_SHEET_IS_FROZEN(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IS_FROZEN)
212 #define GTK_SHEET_IN_XDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_XDRAG)
213 #define GTK_SHEET_IN_YDRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_YDRAG)
214 #define GTK_SHEET_IN_DRAG(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_DRAG)
215 #define GTK_SHEET_IN_SELECTION(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_SELECTION)
216 #define GTK_SHEET_IN_RESIZE(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_RESIZE)
217 #define GTK_SHEET_IN_CLIP(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_CLIP)
218 #define GTK_SHEET_REDRAW_PENDING(sheet) (GTK_SHEET_FLAGS (sheet) & GTK_SHEET_IN_REDRAW_PENDING)
219
220 /* data access macros - no frontend update! */
221
222 #define COLPTR(sheet, colidx) (sheet->column[colidx])
223 #define ROWPTR(sheet, rowidx) (&sheet->row[rowidx])
224
225 #define GTK_SHEET_ROW_IS_VISIBLE(rowptr) ((rowptr)->is_visible)
226 #define GTK_SHEET_ROW_SET_VISIBLE(rowptr, value) ((rowptr)->is_visible = (value))
227 #define GTK_SHEET_ROW_IS_SENSITIVE(rowptr) ((rowptr)->is_sensitive)
228 #define GTK_SHEET_ROW_SET_SENSITIVE(rowptr, value) ((rowptr)->is_sensitive = (value))
229 #define GTK_SHEET_ROW_IS_READONLY(rowptr) (((rowptr)->is_readonly))
230 #define GTK_SHEET_ROW_SET_READONLY(rowptr, value) ((rowptr)->is_readonly = (value))
231 #define GTK_SHEET_ROW_CAN_FOCUS(rowptr) (((rowptr)->can_focus))
232 #define GTK_SHEET_ROW_SET_CAN_FOCUS(rowptr, value) ((rowptr)->can_focus = (value))
233
234 #define GTK_SHEET_ROW_CAN_GRAB_FOCUS(rowptr) \
235 (GTK_SHEET_ROW_IS_VISIBLE(rowptr) ? \
236 (GTK_SHEET_ROW_IS_SENSITIVE(rowptr) ? \
237 GTK_SHEET_ROW_CAN_FOCUS(rowptr) : FALSE) \
238 : FALSE)
239
240 #define GTK_SHEET_CELL_IS_VISIBLE(sheet,row,col) \
241 (gtk_sheet_cell_get_visible(sheet,row,col))
242 #define GTK_SHEET_CELL_IS_SENSITIVE(sheet,row,col) \
243 (gtk_sheet_cell_get_sensitive(sheet,row,col))
244 #define GTK_SHEET_CELL_CAN_FOCUS(sheet,row,col) \
245 (gtk_sheet_cell_get_can_focus(sheet,row,col))
246
247 #define GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col) \
248 (((sheet)->traverse_type == GTK_SHEET_TRAVERSE_ALL) ? TRUE : \
249 gtk_sheet_cell_get_editable((sheet),(row),(col)))
250
251 #define MIN_VIEW_ROW(sheet) (sheet->view.row0)
252 #define MAX_VIEW_ROW(sheet) (sheet->view.rowi) /* beware: MAX_VISIBLE_ROW() can be maxrow+1 */
253 #define MIN_VIEW_COLUMN(sheet) (sheet->view.col0)
254 #define MAX_VIEW_COLUMN(sheet) (sheet->view.coli) /* beware: MAX_VISIBLE_COLUMN() can be maxcol+1 */
255
256 #define COLUMN_UNREALIZED_MAX_WIDTH 512 /* unrealized maximum width */
257 #define COLUMN_REMNANT_PIXELS 32 /* maximized: free space left for others */
258 #define ROW_UNREALIZED_MAX_HEIGHT 128 /* unrealized maximum height */
259 #define ROW_REMNANT_PIXELS 32 /* maximized: free space left for others */
260
261 #define COLUMN_MAX_WIDTH(sheet) \
262 (sheet->sheet_window_width < COLUMN_REMNANT_PIXELS ? \
263 COLUMN_UNREALIZED_MAX_WIDTH : \
264 sheet->sheet_window_width - COLUMN_REMNANT_PIXELS )
265
266 #if 0
267 # define ROW_MAX_HEIGHT(sheet) \
268 (sheet->sheet_window_height < ROW_REMNANT_PIXELS ? \
269 ROW_UNREALIZED_MAX_HEIGHT : \
270 sheet->sheet_window_height - ROW_REMNANT_PIXELS )
271 #else
272 # define ROW_MAX_HEIGHT(sheet) \
273 (sheet->sheet_window_height < ROW_REMNANT_PIXELS ? \
274 ROW_UNREALIZED_MAX_HEIGHT : \
275 sheet->sheet_window_height * 1/3 )
276 #endif
277
278 #define CELL_EXTENT_WIDTH(text_width, attr_border_width) \
279 (text_width + attr_border_width)
280
281 #define CELL_EXTENT_HEIGHT(text_height, attr_border_height) \
282 (text_height + attr_border_height)
283
284 #define COLUMN_EXTENT_TO_WIDTH(extent_width) \
285 (extent_width + 2*CELLOFFSET > COLUMN_MAX_WIDTH(sheet) ? \
286 COLUMN_MAX_WIDTH(sheet) : \
287 extent_width + 2*CELLOFFSET)
288
289 #define ROW_EXTENT_TO_HEIGHT(extent_height) \
290 (extent_height + 2*CELLOFFSET > ROW_MAX_HEIGHT(sheet) ? \
291 ROW_MAX_HEIGHT(sheet) : \
292 extent_height + 2*CELLOFFSET)
293
294 /* GtkSheetRange macros */
295
296 #define _RECT_IN_RANGE(row_0, row_i, col_0, col_i, range) \
297 ((range)->row0 <= (row_0) && (row_i) <= (range)->rowi \
298 && (range)->col0 <= (col_0) && (col_i) <= (range)->coli)
299
300 #define _POINT_IN_RANGE(row, col, range) \
301 _RECT_IN_RANGE(row, row, col, col, range)
302
303 #define _RECT_EQ_RANGE(row_0, row_i, col_0, col_i, range) \
304 ((row_0) == (range)->row0 && (row_i) == (range)->rowi \
305 && (col_0) == (range)->col0 && (col_i) == (range)->coli)
306
307 #define _RECT_NEQ_RANGE(row_0, row_i, col_0, col_i, range) \
308 ((row_0) != (range)->row0 || (row_i) != (range)->rowi \
309 || (col_0) != (range)->col0 || (col_i) != (range)->coli)
310
311 #define _RANGE_EQ_RANGE(range1, range2) \
312 ((range1)->row0 == (range2)->row0 && (range1)->rowi == (range2)->rowi \
313 && (range1)->col0 == (range2)->col0 && (range1)->coli == (range2)->coli)
314
315 #define _RANGE_NEQ_RANGE(range1, range2) \
316 ((range1)->row0) != (range2)->row0 || (range1)->rowi != (range2)->rowi \
317 || (range1)->col0 != (range2)->col0 || (range1)->coli != (range2)->coli)
318
319
320
321
322 /* defaults */
323
324 #define CELL_SPACING 1
325 #define DRAG_WIDTH 6
326 #define TIMEOUT_SCROLL 20
327 #define TIMEOUT_FLASH 200
328 #define TIME_INTERVAL 8
329 #define MINROWS 0
330 #define MINCOLS 0
331 #define MAXLENGTH 30
332 #define CELLOFFSET 4
333
334 #define GTK_SHEET_ROW_DEFAULT_HEIGHT 24
335
336 #define GTK_SHEET_DEFAULT_FONT_ASCENT 12
337 #define GTK_SHEET_DEFAULT_FONT_DESCENT 12
338
339 #define GTK_SHEET_DEFAULT_BG_COLOR "lightgray"
340 #define GTK_SHEET_DEFAULT_GRID_COLOR "gray"
341 #define GTK_SHEET_DEFAULT_TM_COLOR "red" /* tooltip marker */
342 #define GTK_SHEET_DEFAULT_TM_SIZE 4 /* pixels, size of tooltip marker */
343
344 #define GTK_SHEET_PAGE_OVERLAP 1 /* rows to stay visible with PageUp/Dn */
345
346 #ifdef GTK_SHEET_DEBUG
347 # define GTK_SHEET_DEBUG_COLOR "green"
348 static GdkColor debug_color;
349
350 # if 0
351 # include <stdarg.h>
g_debug_popup(char * fmt,...)352 static void g_debug_popup(char *fmt, ...) /* used to intercept/debug drawing sequences */
353 {
354 va_list ap;
355 va_start(ap, fmt);
356
357 GtkWidget *dialog = gtk_message_dialog_new (NULL,
358 GTK_DIALOG_DESTROY_WITH_PARENT,
359 GTK_MESSAGE_ERROR,
360 GTK_BUTTONS_CLOSE,
361 fmt, ap);
362
363 gtk_dialog_run (GTK_DIALOG (dialog));
364 gtk_widget_destroy (dialog);
365 }
366 # endif
367
368 #endif
369
370 /**
371 * _gtk_sheet_row_default_height:
372 * @widget: a #GtkWidget
373 *
374 * Returns: row height in pixels
375 *
376 * calculate row height from font or return default row height
377 * when there is no font associated
378 */
379 guint
_gtk_sheet_row_default_height(GtkWidget * widget)380 _gtk_sheet_row_default_height(GtkWidget *widget)
381 {
382 PangoFontDescription *font_desc =
383 gtk_widget_get_style(GTK_WIDGET(widget))->font_desc;
384
385 if (!font_desc)
386 return (GTK_SHEET_ROW_DEFAULT_HEIGHT);
387
388 PangoContext *context = gtk_widget_get_pango_context(widget);
389
390 PangoFontMetrics *metrics = pango_context_get_metrics(context,
391 font_desc, pango_context_get_language(context));
392 guint val = pango_font_metrics_get_descent(metrics) +
393 pango_font_metrics_get_ascent(metrics);
394 pango_font_metrics_unref(metrics);
395
396 return (PANGO_PIXELS(val) + 2 * CELLOFFSET);
397 }
398
399 static inline guint
_default_font_ascent(GtkWidget * widget)400 _default_font_ascent(GtkWidget *widget)
401 {
402 PangoFontDescription *font_desc =
403 gtk_widget_get_style(GTK_WIDGET(widget))->font_desc;
404
405 if (!font_desc)
406 return (GTK_SHEET_DEFAULT_FONT_ASCENT);
407
408 PangoContext *context = gtk_widget_get_pango_context(widget);
409
410 PangoFontMetrics *metrics = pango_context_get_metrics(context,
411 font_desc, pango_context_get_language(context));
412 guint val = pango_font_metrics_get_ascent(metrics);
413 pango_font_metrics_unref(metrics);
414
415 return (PANGO_PIXELS(val));
416 }
417
_get_string_extent(GtkSheet * sheet,GtkSheetColumn * colptr,PangoFontDescription * font_desc,const gchar * text,guint * width,guint * height)418 static void _get_string_extent(GtkSheet *sheet, GtkSheetColumn *colptr,
419 PangoFontDescription *font_desc, const gchar *text,
420 guint *width, guint *height)
421 {
422 PangoRectangle extent;
423 PangoLayout *layout;
424
425 layout = gtk_widget_create_pango_layout(GTK_WIDGET(sheet), text);
426 pango_layout_set_font_description(layout, font_desc);
427
428 if (colptr && !gtk_sheet_autoresize_columns(sheet))
429 {
430 switch(colptr->wrap_mode)
431 {
432 case GTK_WRAP_NONE:
433 break;
434
435 case GTK_WRAP_CHAR:
436 pango_layout_set_width(layout, colptr->width * PANGO_SCALE);
437 pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
438 break;
439
440 case GTK_WRAP_WORD:
441 pango_layout_set_width(layout, colptr->width * PANGO_SCALE);
442 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
443 break;
444
445 case GTK_WRAP_WORD_CHAR:
446 pango_layout_set_width(layout, colptr->width * PANGO_SCALE);
447 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
448 break;
449 }
450 }
451
452
453 pango_layout_get_pixel_extents(layout, NULL, &extent);
454
455 #if GTK_SHEET_DEBUG_FONT_METRICS > 0
456 {
457 PangoContext *context = gtk_widget_get_pango_context(GTK_WIDGET(sheet));
458 PangoFontMetrics *metrics = pango_context_get_metrics(
459 context, font_desc, pango_context_get_language(context));
460
461 gint ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
462 gint descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
463 gint spacing = pango_layout_get_spacing(layout) / PANGO_SCALE;
464
465 pango_font_metrics_unref(metrics);
466
467 g_debug("_get_string_extent(%s): ext (%d, %d, %d, %d) asc %d desc %d spac %d",
468 text,
469 extent.x, extent.y,
470 extent.width, extent.height,
471 ascent, descent, spacing);
472 }
473 #endif
474
475 g_object_unref(G_OBJECT(layout));
476
477 if (width)
478 *width = extent.width;
479 if (height)
480 *height = extent.height;
481 }
482
483 static inline guint
_default_font_descent(GtkWidget * widget)484 _default_font_descent(GtkWidget *widget)
485 {
486 PangoFontDescription *font_desc =
487 gtk_widget_get_style(GTK_WIDGET(widget))->font_desc;
488
489 if (!font_desc)
490 return (GTK_SHEET_DEFAULT_FONT_DESCENT);
491
492 PangoContext *context = gtk_widget_get_pango_context(widget);
493
494 PangoFontMetrics *metrics = pango_context_get_metrics(context,
495 font_desc, pango_context_get_language(context));
496 guint val = pango_font_metrics_get_descent(metrics);
497 pango_font_metrics_unref(metrics);
498
499 return (PANGO_PIXELS(val));
500 }
501
502 /* gives the top/bottom pixel of the given row in context of the sheet's voffset */
503
504 static inline gint
_gtk_sheet_row_top_ypixel(GtkSheet * sheet,gint row)505 _gtk_sheet_row_top_ypixel(GtkSheet *sheet, gint row)
506 {
507 if (row < 0 || row > sheet->maxrow)
508 return (sheet->voffset);
509 return (sheet->voffset + sheet->row[row].top_ypixel);
510 }
511
512 static inline gint
_gtk_sheet_row_bottom_ypixel(GtkSheet * sheet,gint row)513 _gtk_sheet_row_bottom_ypixel(GtkSheet *sheet, gint row)
514 {
515 gint ypixel = _gtk_sheet_row_top_ypixel(sheet, row);
516 if (0 <= row && row <= sheet->maxrow)
517 ypixel += sheet->row[row].height;
518 return (ypixel);
519 }
520
521
522 /**
523 * _gtk_sheet_row_from_ypixel:
524 * @sheet: the sheet
525 * @y: the pixel
526 *
527 * get row from y pixel location. returns the row index
528 * from a y pixel location (relative to the sheet window)
529 * honoring the sheet's scrolling offset and title visibility.
530 *
531 * beware: the top border belongs to the row, the bottom border to the next
532 *
533 * Returns: row index, -1 or maxcol+1 (beyond right edge)
534 */
535
536 static inline gint
_gtk_sheet_row_from_ypixel(GtkSheet * sheet,gint y)537 _gtk_sheet_row_from_ypixel(GtkSheet *sheet, gint y)
538 {
539 gint i, cy;
540
541 cy = sheet->voffset;
542 if (sheet->column_titles_visible)
543 cy += sheet->column_title_area.height;
544
545 if (y < cy)
546 return (-1); /* top outside */
547
548 for (i = 0; i <= sheet->maxrow; i++)
549 {
550 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
551 {
552 if (cy <= y && y < (cy + sheet->row[i].height))
553 return (i);
554 cy += sheet->row[i].height;
555 }
556 }
557
558 /* no match */
559 return (sheet->maxrow + 1);
560 }
561
562
563
564 /**
565 * _gtk_sheet_column_from_xpixel:
566 * @sheet: the sheet
567 * @x: the pixel
568 *
569 * get column from x pixel location. returns the column index
570 * from a x pixel location (relative to the sheet window)
571 * honoring the sheet's scrolling offset and title visibility.
572 *
573 * beware: the left border belongs to the column, the right border to the next
574 *
575 * Returns: column index, or maxcol+1 (beyond right edge)
576 */
577 static inline gint
_gtk_sheet_column_from_xpixel(GtkSheet * sheet,gint x)578 _gtk_sheet_column_from_xpixel(GtkSheet *sheet, gint x)
579 {
580 gint i, cx;
581
582 cx = sheet->hoffset;
583 if (sheet->row_titles_visible)
584 cx += sheet->row_title_area.width;
585
586 if (x < cx) {
587 return (-1); /* left outside */
588 }
589
590 for (i = 0; i <= sheet->maxcol; i++)
591 {
592 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, i)))
593 {
594 if (cx <= x && x < (cx + COLPTR(sheet, i)->width))
595 return (i);
596 cx += COLPTR(sheet, i)->width;
597 }
598 }
599
600 /* no match */
601 return (sheet->maxcol + 1);
602 }
603
604 /**
605 * _gtk_sheet_first_visible_colidx:
606 * @sheet: the sheet
607 *
608 * find index of leftmost visible column >= startidx
609 *
610 * returns: column index or -1
611 */
_gtk_sheet_first_visible_colidx(GtkSheet * sheet,gint startidx)612 static inline gint _gtk_sheet_first_visible_colidx(GtkSheet *sheet, gint startidx)
613 {
614 gint i;
615 for (i = startidx; i <= sheet->maxcol; i++)
616 {
617 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, i)))
618 return (i);
619 }
620 return (-1);
621 }
622
623 /**
624 * _gtk_sheet_last_visible_colidx:
625 * @sheet: the sheet
626 *
627 * find index of rightmost visible column <= startidx
628 *
629 * returns: column index or -1
630 */
_gtk_sheet_last_visible_colidx(GtkSheet * sheet,gint startidx)631 static inline gint _gtk_sheet_last_visible_colidx(GtkSheet *sheet, gint startidx)
632 {
633 gint i;
634 for (i = startidx; i >= 0; i--)
635 {
636 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, i)))
637 return (i);
638 }
639 return (-1);
640 }
641
642 /**
643 * _gtk_sheet_first_visible_rowidx:
644 * @sheet: the sheet
645 *
646 * find index of topmost visible row >= startidx
647 *
648 * returns: row index or -1
649 */
_gtk_sheet_first_visible_rowidx(GtkSheet * sheet,gint startidx)650 static inline gint _gtk_sheet_first_visible_rowidx(GtkSheet *sheet, gint startidx)
651 {
652 gint i;
653 for (i = startidx; i <= sheet->maxrow; i++)
654 {
655 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
656 return (i);
657 }
658 return (-1);
659 }
660
661 /**
662 * _gtk_sheet_last_visible_rowidx:
663 * @sheet: the sheet
664 *
665 * find index of bottommost visible row <= startidx
666 *
667 * returns: row index or -1
668 */
_gtk_sheet_last_visible_rowidx(GtkSheet * sheet,gint startidx)669 static inline gint _gtk_sheet_last_visible_rowidx(GtkSheet *sheet, gint startidx)
670 {
671 gint i;
672 for (i = startidx; i >= 0; i--)
673 {
674 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
675 return (i);
676 }
677 return (-1);
678 }
679
680 /**
681 * _gtk_sheet_get_visible_range:
682 * @sheet: the sheet
683 * @visr: pointer to store results
684 *
685 * return visible sheet area
686 * [first visible row/col .. last visible row/col]
687 *
688 * returns: TRUE if any visible cells exist and range is valid
689 */
_gtk_sheet_get_visible_range(GtkSheet * sheet,GtkSheetRange * visr)690 static inline gboolean _gtk_sheet_get_visible_range(GtkSheet *sheet,
691 GtkSheetRange *visr)
692 {
693 visr->row0 = visr->rowi = visr->col0 = visr->coli = -1;
694
695 visr->row0 = _gtk_sheet_first_visible_rowidx(sheet, 0);
696 if (visr->row0 < 0)
697 return (FALSE);
698
699 visr->rowi = _gtk_sheet_last_visible_rowidx(sheet, sheet->maxrow);
700 if (visr->rowi < 0)
701 return (FALSE);
702
703 visr->col0 = _gtk_sheet_first_visible_colidx(sheet, 0);
704 if (visr->col0 < 0)
705 return (FALSE);
706
707 visr->coli = _gtk_sheet_last_visible_colidx(sheet, sheet->maxcol);
708 if (visr->coli < 0)
709 return (FALSE);
710
711 return (TRUE);
712 }
713
714 /**
715 * _gtk_sheet_count_visible:
716 * @sheet: the sheet
717 * @range: the #GtkSheetRange to inspect
718 * @nrows: number of visible rows in range (out)
719 * @ncols: number of visible columns in range (out)
720 *
721 * count visible rows/cols in range
722 */
_gtk_sheet_count_visible(GtkSheet * sheet,GtkSheetRange * range,gint * nrows,gint * ncols)723 static inline void _gtk_sheet_count_visible(GtkSheet *sheet,
724 GtkSheetRange *range, gint *nrows, gint *ncols)
725 {
726 gint i;
727 *nrows = *ncols = 0;
728
729 for (i = range->row0; i <= range->rowi; i++)
730 {
731 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
732 ++(*nrows);
733 }
734 for (i = range->col0; i <= range->coli; i++)
735 {
736 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, i)))
737 ++(*ncols);
738 }
739 }
740
741
742
743 /**
744 * gtk_sheet_height:
745 * @sheet: the #GtkSheet
746 *
747 * returns the total height of the sheet
748 *
749 * returns: total height of all visible rows, including
750 * col_titles area
751 */
752 gint
gtk_sheet_height(GtkSheet * sheet)753 gtk_sheet_height(GtkSheet *sheet)
754 {
755 gint i, cx;
756
757 cx = (sheet->column_titles_visible ? sheet->column_title_area.height : 0);
758
759 for (i = 0; i <= sheet->maxrow; i++)
760 {
761 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
762 cx += sheet->row[i].height;
763 }
764
765 return (cx);
766 }
767
768 /**
769 * gtk_sheet_width:
770 * @sheet: the #GtkSheet
771 *
772 * total width of the sheet
773 *
774 * returns: total width of all visible columns, including
775 * row_titles area
776 */
777 gint
gtk_sheet_width(GtkSheet * sheet)778 gtk_sheet_width(GtkSheet *sheet)
779 {
780 gint i, cx;
781
782 cx = (sheet->row_titles_visible ? sheet->row_title_area.width : 0);
783
784 for (i = 0; i <= sheet->maxcol; i++)
785 {
786 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, i)))
787 cx += COLPTR(sheet, i)->width;
788 }
789
790 return (cx);
791 }
792
793 /**
794 * _gtk_sheet_recalc_view_range:
795 * @sheet: the #GtkSheet
796 *
797 * recalculate visible sheet range
798 */
799 void
_gtk_sheet_recalc_view_range(GtkSheet * sheet)800 _gtk_sheet_recalc_view_range(GtkSheet *sheet)
801 {
802 sheet->view.row0 = _gtk_sheet_row_from_ypixel(sheet,
803 sheet->column_titles_visible ? sheet->column_title_area.height : 0);
804 sheet->view.rowi = _gtk_sheet_row_from_ypixel(sheet,
805 sheet->sheet_window_height - 1);
806
807 sheet->view.col0 = _gtk_sheet_column_from_xpixel(sheet,
808 sheet->row_titles_visible ? sheet->row_title_area.width : 0);
809 sheet->view.coli = _gtk_sheet_column_from_xpixel(sheet,
810 sheet->sheet_window_width - 1);
811 }
812
813 /**
814 * _gtk_sheet_range_fixup:
815 *
816 * @param sheet the #GtkSheet
817 * @param range the range
818 *
819 * force range into bounds
820 */
821 void
_gtk_sheet_range_fixup(GtkSheet * sheet,GtkSheetRange * range)822 _gtk_sheet_range_fixup(GtkSheet *sheet, GtkSheetRange *range)
823 {
824 if (range->row0 < 0)
825 range->row0 = 0;
826 if (range->rowi > sheet->maxrow)
827 range->rowi = sheet->maxrow;
828 if (range->col0 < 0)
829 range->col0 = 0;
830 if (range->coli > sheet->maxcol)
831 range->coli = sheet->maxcol;
832 }
833
834 /*
835 * POSSIBLE_XDRAG
836 *
837 * Drag grip test for column width dragging
838 *
839 * @param sheet
840 * @param x
841 * @param drag_column
842 *
843 * @return
844 */
845 static inline gint
POSSIBLE_XDRAG(GtkSheet * sheet,gint x,gint * drag_column)846 POSSIBLE_XDRAG(GtkSheet *sheet, gint x, gint *drag_column)
847 {
848 gint column, xdrag;
849
850 column = _gtk_sheet_column_from_xpixel(sheet, x);
851 if (column < 0 || column > sheet->maxcol)
852 return (FALSE);
853
854 xdrag = _gtk_sheet_column_left_xpixel(sheet, column);
855
856 if (column > 0 && x <= xdrag + DRAG_WIDTH / 2) /* you pick it at the left border */
857 {
858 while (column > 0 && !GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, column - 1))) column--;
859
860 --column; /* you really want to resize the column on the left side */
861
862 if (column < 0 || column > sheet->maxcol)
863 return (FALSE);
864
865 *drag_column = column;
866 return (TRUE);
867 #if 0
868 return(GTK_SHEET_COLUMN_IS_SENSITIVE(COLPTR(sheet, column)));
869 #endif
870 }
871
872 xdrag = _gtk_sheet_column_right_xpixel(sheet, column);
873
874 if (xdrag - DRAG_WIDTH / 2 <= x && x <= xdrag + DRAG_WIDTH / 2)
875 {
876 *drag_column = column;
877 return (TRUE);
878 #if 0
879 return(GTK_SHEET_COLUMN_IS_SENSITIVE(COLPTR(sheet, column)));
880 #endif
881 }
882
883 return (FALSE);
884 }
885
886 /*
887 * POSSIBLE_YDRAG
888 *
889 * Drag grip test for row height dragging
890 *
891 * @param sheet
892 * @param y
893 * @param drag_row
894 *
895 * @return
896 */
897 static inline gint
POSSIBLE_YDRAG(GtkSheet * sheet,gint y,gint * drag_row)898 POSSIBLE_YDRAG(GtkSheet *sheet, gint y, gint *drag_row)
899 {
900 gint row, ydrag;
901
902 row = _gtk_sheet_row_from_ypixel(sheet, y);
903 if (row < 0 || row > sheet->maxrow)
904 return (FALSE);
905
906 ydrag = _gtk_sheet_row_top_ypixel(sheet, row);
907
908 if (row > 0 && y <= ydrag + DRAG_WIDTH / 2) /* you pick it at the top border */
909 {
910 while (row > 0 && !GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row - 1))) row--;
911
912 --row; /* you really want to resize the row above */
913
914 if (row < 0 || row > sheet->maxrow)
915 return (FALSE);
916
917 *drag_row = row;
918 return (TRUE);
919 #if 0
920 return(GTK_SHEET_ROW_IS_SENSITIVE(ROWPTR(sheet, row)));
921 #endif
922 }
923
924 ydrag = _gtk_sheet_row_bottom_ypixel(sheet, row);
925
926 if (ydrag - DRAG_WIDTH / 2 <= y && y <= ydrag + DRAG_WIDTH / 2)
927 {
928 *drag_row = row;
929 return (TRUE);
930 #if 0
931 return(GTK_SHEET_ROW_IS_SENSITIVE(ROWPTR(sheet, row)));
932 #endif
933 }
934
935 return (FALSE);
936 }
937
938 /*
939 * POSSIBLE_DRAG
940 *
941 * Dragging grip test for a cell range
942 *
943 * @param sheet
944 * @param x
945 * @param y
946 * @param drag_row
947 * @param drag_column
948 *
949 * @return
950 */
951 static inline gint
POSSIBLE_DRAG(GtkSheet * sheet,gint x,gint y,gint * drag_row,gint * drag_column)952 POSSIBLE_DRAG(GtkSheet *sheet, gint x, gint y, gint *drag_row, gint *drag_column)
953 {
954 gint ydrag, xdrag;
955
956 *drag_column = _gtk_sheet_column_from_xpixel(sheet, x);
957 *drag_row = _gtk_sheet_row_from_ypixel(sheet, y);
958
959 /* drag grip at top/bottom border */
960
961 if (x >= _gtk_sheet_column_left_xpixel(sheet, sheet->range.col0) - DRAG_WIDTH / 2 &&
962 x <= _gtk_sheet_column_right_xpixel(sheet, sheet->range.coli) + DRAG_WIDTH / 2)
963 {
964 ydrag = _gtk_sheet_row_top_ypixel(sheet, sheet->range.row0);
965
966 if (ydrag - DRAG_WIDTH / 2 <= y && y <= ydrag + DRAG_WIDTH / 2)
967 {
968 *drag_row = sheet->range.row0;
969 return (TRUE);
970 }
971
972 ydrag = _gtk_sheet_row_bottom_ypixel(sheet, sheet->range.rowi);
973
974 if (ydrag - DRAG_WIDTH / 2 <= y && y <= ydrag + DRAG_WIDTH / 2)
975 {
976 *drag_row = sheet->range.rowi;
977 return (TRUE);
978 }
979
980 }
981
982 /* drag grip at left/right border */
983
984 if (y >= _gtk_sheet_row_top_ypixel(sheet, sheet->range.row0) - DRAG_WIDTH / 2 &&
985 y <= _gtk_sheet_row_bottom_ypixel(sheet, sheet->range.rowi) + DRAG_WIDTH / 2)
986 {
987 xdrag = _gtk_sheet_column_left_xpixel(sheet, sheet->range.col0);
988
989 if (xdrag - DRAG_WIDTH / 2 <= x && x <= xdrag + DRAG_WIDTH / 2)
990 {
991 *drag_column = sheet->range.col0;
992 return (TRUE);
993 }
994
995 xdrag = _gtk_sheet_column_right_xpixel(sheet, sheet->range.coli);
996
997 if (xdrag - DRAG_WIDTH / 2 <= x && x <= xdrag + DRAG_WIDTH / 2)
998 {
999 *drag_column = sheet->range.coli;
1000 return (TRUE);
1001 }
1002 }
1003
1004 return (FALSE);
1005 }
1006
1007 static inline gint
POSSIBLE_RESIZE(GtkSheet * sheet,gint x,gint y,gint * drag_row,gint * drag_column)1008 POSSIBLE_RESIZE(GtkSheet *sheet, gint x, gint y, gint *drag_row, gint *drag_column)
1009 {
1010 gint xdrag, ydrag;
1011
1012 xdrag = _gtk_sheet_column_right_xpixel(sheet, sheet->range.coli);
1013 ydrag = _gtk_sheet_row_bottom_ypixel(sheet, sheet->range.rowi);
1014
1015 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
1016 ydrag = _gtk_sheet_row_top_ypixel(sheet, sheet->view.row0);
1017
1018 if (sheet->state == GTK_SHEET_ROW_SELECTED)
1019 xdrag = _gtk_sheet_column_left_xpixel(sheet, sheet->view.col0);
1020
1021 *drag_column = _gtk_sheet_column_from_xpixel(sheet, x);
1022 *drag_row = _gtk_sheet_row_from_ypixel(sheet, y);
1023
1024 if (xdrag - DRAG_WIDTH / 2 <= x && x <= xdrag + DRAG_WIDTH / 2 &&
1025 ydrag - DRAG_WIDTH / 2 <= y && y <= ydrag + DRAG_WIDTH / 2)
1026 {
1027 return (TRUE);
1028 }
1029
1030 return (FALSE);
1031 }
1032
1033 /* Prototypes (extern) */
1034
1035 extern void _gtkextra_signal_emit(GtkObject *object, guint signal_id, ...);
1036
1037 /* Prototypes (static) */
1038
1039 static void gtk_sheet_class_init(GtkSheetClass *klass);
1040 static void gtk_sheet_init(GtkSheet *sheet);
1041 static void gtk_sheet_destroy_handler(GtkObject *object);
1042 static void gtk_sheet_finalize_handler(GObject *object);
1043 static void gtk_sheet_style_set_handler(GtkWidget *widget,
1044 GtkStyle *previous_style);
1045 static void gtk_sheet_realize_handler(GtkWidget *widget);
1046 static void gtk_sheet_unrealize_handler(GtkWidget *widget);
1047 static void gtk_sheet_map_handler(GtkWidget *widget);
1048 static void gtk_sheet_unmap_handler(GtkWidget *widget);
1049
1050 static gboolean gtk_sheet_expose_handler(GtkWidget *widget,
1051 GdkEventExpose *event);
1052
1053 static void gtk_sheet_forall_handler(GtkContainer *container,
1054 gboolean include_internals,
1055 GtkCallback callback,
1056 gpointer callback_data);
1057
1058 static void gtk_sheet_set_scroll_adjustments(GtkSheet *sheet,
1059 GtkAdjustment *hadjustment,
1060 GtkAdjustment *vadjustment);
1061
1062 static gboolean gtk_sheet_button_press_handler(GtkWidget *widget,
1063 GdkEventButton *event);
1064 static gboolean gtk_sheet_button_release_handler(GtkWidget *widget,
1065 GdkEventButton *event);
1066 static gboolean gtk_sheet_motion_handler(GtkWidget *widget,
1067 GdkEventMotion *event);
1068
1069 static gboolean gtk_sheet_entry_key_press_handler(GtkWidget *widget,
1070 GdkEventKey *key,
1071 gpointer user_data);
1072
1073 static gboolean gtk_sheet_key_press_handler(GtkWidget *widget,
1074 GdkEventKey *key);
1075
1076 static void gtk_sheet_size_request_handler(GtkWidget *widget,
1077 GtkRequisition *requisition);
1078 static void gtk_sheet_size_allocate_handler(GtkWidget *widget,
1079 GtkAllocation *allocation);
1080
1081 static gboolean gtk_sheet_focus(GtkWidget *widget,
1082 GtkDirectionType direction);
1083
1084 static void _gtk_sheet_move_cursor(GtkSheet *sheet,
1085 GtkMovementStep step,
1086 gint count,
1087 gboolean extend_selection);
1088
1089 /* Sheet queries */
1090
1091 static gint gtk_sheet_range_isvisible(GtkSheet *sheet, GtkSheetRange range);
1092 static gint gtk_sheet_cell_isvisible(GtkSheet *sheet, gint row, gint column);
1093
1094 /* Clipped Range */
1095
1096 static gint _gtk_sheet_scroll_to_pointer(gpointer data);
1097 static gint gtk_sheet_flash(gpointer data);
1098
1099 /* Drawing Routines */
1100
1101 /* draw cell background and frame */
1102 static void _cell_draw_background(GtkSheet *sheet, gint row, gint column);
1103
1104 /* draw cell border */
1105 static void _cell_draw_border(GtkSheet *sheet,
1106 gint row, gint column, gint mask);
1107
1108 /* draw cell contents */
1109 static void _cell_draw_label(GtkSheet *sheet, gint row, gint column);
1110
1111 /* highlight the visible part of the selected range */
1112 static void gtk_sheet_range_draw_selection(GtkSheet *sheet, GtkSheetRange range);
1113
1114 /* Selection */
1115
1116 static gint _gtk_sheet_move_query(GtkSheet *sheet, gint row, gint column, gboolean need_focus);
1117 static void gtk_sheet_real_select_range(GtkSheet *sheet, GtkSheetRange *range);
1118 static void gtk_sheet_real_unselect_range(GtkSheet *sheet, GtkSheetRange *range);
1119 static void gtk_sheet_extend_selection(GtkSheet *sheet, gint row, gint column);
1120 static void gtk_sheet_new_selection(GtkSheet *sheet, GtkSheetRange *range);
1121 static void gtk_sheet_draw_border(GtkSheet *sheet, GtkSheetRange range);
1122 static void gtk_sheet_draw_corners(GtkSheet *sheet, GtkSheetRange range);
1123
1124 /* Active Cell handling */
1125
1126 static void gtk_sheet_entry_changed_handler(GtkWidget *widget, gpointer data);
1127 static gboolean gtk_sheet_deactivate_cell(GtkSheet *sheet);
1128 static gboolean gtk_sheet_activate_cell(GtkSheet *sheet, gint row, gint col);
1129 static void gtk_sheet_draw_active_cell(GtkSheet *sheet);
1130 static void gtk_sheet_show_active_cell(GtkSheet *sheet);
1131 static void gtk_sheet_click_cell(GtkSheet *sheet, gint row, gint column, gboolean *veto);
1132
1133 /* Backing Pixmap */
1134
1135 static void gtk_sheet_make_backing_pixmap(GtkSheet *sheet, guint width, guint height);
1136 static void gtk_sheet_draw_backing_pixmap(GtkSheet *sheet, GtkSheetRange range);
1137 /* Scrollbars */
1138
1139 static void _vadjustment_changed_handler(GtkAdjustment *adjustment, gpointer data);
1140 static void _hadjustment_changed_handler(GtkAdjustment *adjustment, gpointer data);
1141 static void _vadjustment_value_changed_handler(GtkAdjustment *adjustment, gpointer data);
1142 static void _hadjustment_value_changed_handler(GtkAdjustment *adjustment, gpointer data);
1143
1144 static void draw_xor_vline(GtkSheet *sheet);
1145 static void draw_xor_hline(GtkSheet *sheet);
1146 static void draw_xor_rectangle(GtkSheet *sheet, GtkSheetRange range);
1147 static void gtk_sheet_draw_flashing_range(GtkSheet *sheet, GtkSheetRange range);
1148 static guint new_column_width(GtkSheet *sheet, gint column, gint *x);
1149 static guint new_row_height(GtkSheet *sheet, gint row, gint *y);
1150
1151 /* Sheet Button */
1152
1153 static void create_global_button(GtkSheet *sheet);
1154
1155 /* Sheet Entry */
1156
1157 static void create_sheet_entry(GtkSheet *sheet, GType new_entry_type);
1158 static void gtk_sheet_entry_set_max_size(GtkSheet *sheet);
1159
1160 /* Sheet button gadgets */
1161
1162 static void size_allocate_row_title_buttons(GtkSheet *sheet);
1163
1164 static void row_button_set(GtkSheet *sheet, gint row);
1165 static void row_button_release(GtkSheet *sheet, gint row);
1166 static void size_allocate_global_button(GtkSheet *sheet);
1167
1168 /* Attributes routines */
1169
1170 static void gtk_sheet_set_cell_attributes(GtkSheet *sheet,
1171 gint row, gint col, GtkSheetCellAttr attributes);
1172 static void init_attributes(GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes);
1173
1174 /* Memory allocation routines */
1175 static void gtk_sheet_real_range_clear(GtkSheet *sheet,
1176 const GtkSheetRange *range, gboolean delete);
1177 static void gtk_sheet_real_cell_clear(GtkSheet *sheet,
1178 gint row, gint column, gboolean delete);
1179
1180 static GtkSheetCell *gtk_sheet_cell_new(void);
1181
1182 static void AddRows(GtkSheet *sheet, gint position, gint nrows);
1183 static void AddColumns(GtkSheet *sheet, gint position, gint ncols);
1184 static void InsertRow(GtkSheet *sheet, gint row, gint nrows);
1185 static void InsertColumn(GtkSheet *sheet, gint col, gint ncols);
1186 static void DeleteRow(GtkSheet *sheet, gint row, gint nrows);
1187 static void DeleteColumn(GtkSheet *sheet, gint col, gint ncols);
1188 static gint GrowSheet(GtkSheet *sheet, gint newrows, gint newcols);
1189 static void CheckBounds(GtkSheet *sheet, gint row, gint col);
1190 static void CheckCellData(GtkSheet *sheet, const gint row, const gint col);
1191
1192 /* Container Functions */
1193 static void gtk_sheet_remove_handler(GtkContainer *container, GtkWidget *widget);
1194 static void gtk_sheet_realize_child(GtkSheet *sheet, GtkSheetChild *child);
1195 static void gtk_sheet_position_child(GtkSheet *sheet, GtkSheetChild *child);
1196 static void gtk_sheet_position_children(GtkSheet *sheet);
1197 static void gtk_sheet_row_size_request(GtkSheet *sheet, gint row, guint *requisition);
1198
1199 /* GtkBuildableIface */
1200
1201
1202 /*
1203 * gtk_sheet_buildable_add_child_internal
1204 *
1205 * embed a foreign created #GtkSheetColumn into an existing #GtkSheet
1206 * Used by GtkBuilder and Glade.
1207 *
1208 * @param sheet The sheet where the column should be added
1209 * @param child The column to be added
1210 * @param name The #GtkWidget name of the column
1211 */
1212
1213 void
gtk_sheet_buildable_add_child_internal(GtkSheet * sheet,GtkSheetColumn * child,const char * name)1214 gtk_sheet_buildable_add_child_internal(GtkSheet *sheet,
1215 GtkSheetColumn *child,
1216 const char *name)
1217 {
1218 int col;
1219
1220 g_return_if_fail(GTK_IS_SHEET(sheet));
1221 g_return_if_fail(GTK_IS_SHEET_COLUMN(child));
1222
1223 gtk_sheet_add_column(sheet, 1);
1224 col = gtk_sheet_get_columns_count(sheet) - 1;
1225
1226 if (sheet->column[col])
1227 {
1228 COLPTR(sheet, col)->sheet = NULL;
1229
1230 g_object_unref(sheet->column[col]);
1231 sheet->column[col] = NULL;
1232 }
1233
1234 child->sheet = sheet;
1235 sheet->column[col] = child;
1236
1237 g_object_ref_sink(G_OBJECT(child));
1238
1239 #if GTK_SHEET_DEBUG_BUILDER > 0
1240 g_debug("gtk_sheet_buildable_add_child_internal: %s, m %d r %d v %d",
1241 name ? name : "NULL",
1242 gtk_widget_get_mapped(GTK_WIDGET(child)),
1243 gtk_widget_get_realized(GTK_WIDGET(child)),
1244 gtk_widget_get_visible(GTK_WIDGET(child))
1245 );
1246 #endif
1247
1248 /* we always set the parent in order to track into what sheet the column belongs,
1249 which leads to problems with invisible columns.
1250 When trying to set them visible, Gtk 2.24.5 terminates the application with
1251 ? Gtk - gtk_widget_realize: assertion `GTK_WIDGET_ANCHORED (widget) || GTK_IS_INVISIBLE (widget)' failed
1252 see also the fix in gtk_sheet_column_set_visibility()
1253 */
1254 gtk_widget_set_parent(GTK_WIDGET(child), GTK_WIDGET(sheet));
1255
1256 if (name)
1257 gtk_widget_set_name(GTK_WIDGET(child), name);
1258
1259 _gtk_sheet_reset_text_column(sheet, col);
1260 _gtk_sheet_recalc_left_xpixels(sheet);
1261 }
1262
1263 static void
gtk_sheet_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)1264 gtk_sheet_buildable_add_child(
1265 GtkBuildable *buildable,
1266 GtkBuilder *builder,
1267 GObject *child,
1268 const gchar *type)
1269 {
1270 GtkSheet *sheet;
1271 GtkSheetColumn *newcol;
1272 const gchar *name = gtk_widget_get_name(GTK_WIDGET(child));
1273
1274 #if GTK_SHEET_DEBUG_BUILDER > 0
1275 g_debug("gtk_sheet_buildable_add_child %p: %s type %s", child,
1276 name ? name : "NULL",
1277 type ? type : "NULL");
1278 #endif
1279
1280 sheet = GTK_SHEET(buildable);
1281 newcol = GTK_SHEET_COLUMN(child);
1282
1283 #if GTK_SHEET_DEBUG_BUILDER > 0
1284 {
1285 gchar *strval;
1286
1287 g_object_get(G_OBJECT(newcol), "label", &strval, NULL);
1288 g_debug("gtk_sheet_buildable_add_child: label=%s", strval ? strval : "NULL");
1289
1290 g_free(strval);
1291 }
1292 #endif
1293
1294 gtk_sheet_buildable_add_child_internal(sheet, newcol, name);
1295 }
1296
1297 static void
gtk_sheet_buildable_init(GtkBuildableIface * iface)1298 gtk_sheet_buildable_init(GtkBuildableIface *iface)
1299 {
1300 #if GTK_SHEET_DEBUG_BUILDER > 0
1301 g_debug("gtk_sheet_buildable_init");
1302 #endif
1303 iface->add_child = gtk_sheet_buildable_add_child;
1304 }
1305
1306 static GtkSheetArea
gtk_sheet_get_area_at(GtkSheet * sheet,gint x,gint y)1307 gtk_sheet_get_area_at(GtkSheet *sheet, gint x, gint y)
1308 {
1309 if (sheet->column_titles_visible && (y < sheet->column_title_area.height))
1310 {
1311 if (sheet->row_titles_visible && (x < sheet->row_title_area.width))
1312 return (ON_SHEET_BUTTON_AREA);
1313
1314 return (ON_COLUMN_TITLES_AREA);
1315 }
1316 else
1317 {
1318 if (sheet->row_titles_visible && (x < sheet->row_title_area.width))
1319 return (ON_ROW_TITLES_AREA);
1320 }
1321 return (ON_CELL_AREA);
1322 }
1323
1324 /*
1325 * gtk_sheet_query_tooltip - tooltip handler
1326 *
1327 * Preference rule for the tooltip search is cell->row->column->sheet
1328 *
1329 * @param widget
1330 * @param x
1331 * @param y
1332 * @param keyboard_mode
1333 * @param tooltip
1334 * @param user_data
1335 *
1336 * @return TRUE or FALSE wether to show the tip or not
1337 */
1338 static gboolean
gtk_sheet_query_tooltip_handler(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)1339 gtk_sheet_query_tooltip_handler(GtkWidget *widget,
1340 gint x, gint y,
1341 gboolean keyboard_mode,
1342 GtkTooltip *tooltip,
1343 gpointer user_data)
1344 {
1345 gchar *tip;
1346 GtkSheetArea area;
1347 gint row = -1, col = -1;
1348 GtkSheet *sheet = GTK_SHEET(widget);
1349
1350 if (!sheet)
1351 return (FALSE);
1352
1353 area = gtk_sheet_get_area_at(sheet, x, y);
1354
1355 if (area == ON_CELL_AREA)
1356 {
1357 if (row < 0)
1358 row = _gtk_sheet_row_from_ypixel(sheet, y);
1359 if (col < 0)
1360 col = _gtk_sheet_column_from_xpixel(sheet, x);
1361
1362 if ((0 <= row && row <= sheet->maxrow && 0 <= col && col <= sheet->maxcol)
1363 && (row <= sheet->maxallocrow && col <= sheet->maxalloccol)
1364 && (sheet->data[row] && sheet->data[row][col]))
1365 {
1366 GtkSheetCell *cell = sheet->data[row][col];
1367
1368 tip = cell->tooltip_markup;
1369 if (tip && tip[0])
1370 {
1371 gtk_tooltip_set_markup(tooltip, tip);
1372 return (TRUE);
1373 }
1374
1375 tip = cell->tooltip_text;
1376 if (tip && tip[0])
1377 {
1378 gtk_tooltip_set_text(tooltip, tip);
1379 return (TRUE);
1380 }
1381 }
1382
1383 area = ON_ROW_TITLES_AREA; /* fallback */
1384 }
1385
1386 if (area == ON_ROW_TITLES_AREA)
1387 {
1388 if (row < 0)
1389 row = _gtk_sheet_row_from_ypixel(sheet, y);
1390
1391 if (0 <= row && row <= sheet->maxrow)
1392 {
1393 GtkSheetRow *rowp = ROWPTR(sheet, row);
1394
1395 tip = rowp->tooltip_markup;
1396 if (tip && tip[0])
1397 {
1398 gtk_tooltip_set_markup(tooltip, tip);
1399 return (TRUE);
1400 }
1401
1402 tip = rowp->tooltip_text;
1403 if (tip && tip[0])
1404 {
1405 gtk_tooltip_set_text(tooltip, tip);
1406 return (TRUE);
1407 }
1408 }
1409
1410 area = ON_COLUMN_TITLES_AREA; /* fallback */
1411 }
1412
1413 if (area == ON_COLUMN_TITLES_AREA)
1414 {
1415 if (col < 0)
1416 col = _gtk_sheet_column_from_xpixel(sheet, x);
1417
1418 if (0 <= col && col <= sheet->maxcol)
1419 {
1420 GtkSheetColumn *column = COLPTR(sheet, col);
1421
1422 tip = gtk_widget_get_tooltip_markup(GTK_WIDGET(column));
1423 if (tip && tip[0])
1424 {
1425 gtk_tooltip_set_markup(tooltip, tip);
1426 g_free(tip);
1427 return (TRUE);
1428 }
1429
1430 tip = gtk_widget_get_tooltip_text(GTK_WIDGET(column));
1431 if (tip && tip[0])
1432 {
1433 gtk_tooltip_set_text(tooltip, tip);
1434 g_free(tip);
1435 return (TRUE);
1436 }
1437 }
1438 }
1439
1440 /* fallback to sheet tip */
1441
1442 tip = gtk_widget_get_tooltip_markup(widget);
1443 if (tip && tip[0])
1444 {
1445 gtk_tooltip_set_markup(tooltip, tip);
1446 g_free(tip);
1447 return (TRUE);
1448 }
1449
1450 tip = gtk_widget_get_tooltip_text(widget);
1451 if (tip && tip[0])
1452 {
1453 gtk_tooltip_set_text(tooltip, tip);
1454 g_free(tip);
1455 return (TRUE);
1456 }
1457
1458 return (FALSE);
1459 }
1460
1461 /* Type initialisation */
1462
1463 static GtkContainerClass *sheet_parent_class = NULL;
1464
1465 GType
gtk_sheet_get_type(void)1466 gtk_sheet_get_type(void)
1467 {
1468 static GType sheet_type = 0;
1469
1470 if (!sheet_type)
1471 {
1472 static const GTypeInfo sheet_info =
1473 {
1474 sizeof(GtkSheetClass),
1475 NULL,
1476 NULL,
1477 (GClassInitFunc)gtk_sheet_class_init,
1478 NULL,
1479 NULL,
1480 sizeof(GtkSheet),
1481 0,
1482 (GInstanceInitFunc)gtk_sheet_init,
1483 NULL,
1484 };
1485
1486 static const GInterfaceInfo interface_info = {
1487 (GInterfaceInitFunc)gtk_sheet_buildable_init,
1488 (GInterfaceFinalizeFunc)NULL,
1489 (gpointer)NULL
1490 };
1491
1492 sheet_type = g_type_register_static(gtk_container_get_type(),
1493 "GtkSheet",
1494 &sheet_info,
1495 0);
1496
1497 g_type_add_interface_static(sheet_type, GTK_TYPE_BUILDABLE,
1498 &interface_info);
1499 }
1500 return (sheet_type);
1501 }
1502
1503
1504
1505 static GtkSheetRange *
gtk_sheet_range_copy(const GtkSheetRange * range)1506 gtk_sheet_range_copy(const GtkSheetRange *range)
1507 {
1508 GtkSheetRange *new_range;
1509
1510 g_return_val_if_fail(range != NULL, NULL);
1511
1512 new_range = g_new(GtkSheetRange, 1);
1513
1514 *new_range = *range;
1515
1516 return (new_range);
1517 }
1518
1519 static void
gtk_sheet_range_free(GtkSheetRange * range)1520 gtk_sheet_range_free(GtkSheetRange *range)
1521 {
1522 g_return_if_fail(range != NULL);
1523
1524 g_free(range);
1525 }
1526
1527 GType
gtk_sheet_range_get_type(void)1528 gtk_sheet_range_get_type(void)
1529 {
1530 static GType sheet_range_type = 0;
1531
1532 if (!sheet_range_type)
1533 {
1534 sheet_range_type = g_boxed_type_register_static("GtkSheetRange", (GBoxedCopyFunc)gtk_sheet_range_copy, (GBoxedFreeFunc)gtk_sheet_range_free);
1535 }
1536 return (sheet_range_type);
1537 }
1538
1539 /**
1540 * _gtk_sheet_entry_type_from_gtype:
1541 * @entry_type: type to be mapped
1542 *
1543 * map gtype to #GtkSheetEntryType
1544 *
1545 * Returns: #GtkSheetEntryType or #GTK_SHEET_ENTRY_TYPE_DEFAULT
1546 */
1547 GtkSheetEntryType
_gtk_sheet_entry_type_from_gtype(GType entry_type)1548 _gtk_sheet_entry_type_from_gtype(GType entry_type)
1549 {
1550 if (entry_type == G_TYPE_ITEM_ENTRY)
1551 return (GTK_SHEET_ENTRY_TYPE_GTK_ITEM_ENTRY);
1552
1553 else if (entry_type == GTK_TYPE_ENTRY)
1554 return (GTK_SHEET_ENTRY_TYPE_GTK_ITEM_ENTRY);
1555
1556 else if (entry_type == GTK_TYPE_TEXT_VIEW)
1557 return (GTK_SHEET_ENTRY_TYPE_GTK_TEXT_VIEW);
1558
1559 else if (entry_type == GTK_TYPE_DATA_TEXT_VIEW)
1560 return (GTK_SHEET_ENTRY_TYPE_GTK_DATA_TEXT_VIEW);
1561
1562 else if (entry_type == GTK_TYPE_SPIN_BUTTON)
1563 return (GTK_SHEET_ENTRY_TYPE_GTK_SPIN_BUTTON);
1564
1565 else if (entry_type == GTK_TYPE_COMBO_BOX)
1566 return (GTK_SHEET_ENTRY_TYPE_GTK_COMBO_BOX);
1567
1568 #if !GTK_CHECK_VERSION(2,24,0)
1569 else if (entry_type == GTK_TYPE_COMBO_BOX_ENTRY)
1570 return (GTK_SHEET_ENTRY_TYPE_GTK_COMBO_BOX_ENTRY);
1571 #endif
1572
1573 #if !GTK_CHECK_VERSION(2,4,0)
1574 else if (entry_type == GTK_TYPE_COMBO)
1575 return (GTK_SHEET_ENTRY_TYPE_GTK_COMBO);
1576 #endif
1577
1578 return (GTK_SHEET_ENTRY_TYPE_DEFAULT);
1579 }
1580
1581 /**
1582 * _gtk_sheet_entry_type_to_gtype:
1583 * @ety: type to be mapped
1584 *
1585 * map #GType to #GtkSheetEntryType
1586 *
1587 * Returns: #GType or #G_TYPE_NONE
1588 */
1589 GType
_gtk_sheet_entry_type_to_gtype(GtkSheetEntryType ety)1590 _gtk_sheet_entry_type_to_gtype(GtkSheetEntryType ety)
1591 {
1592 switch(ety)
1593 {
1594 case GTK_SHEET_ENTRY_TYPE_GTK_ITEM_ENTRY:
1595 return (G_TYPE_ITEM_ENTRY);
1596
1597 case GTK_SHEET_ENTRY_TYPE_GTK_ENTRY:
1598 return (GTK_TYPE_ENTRY);
1599
1600 case GTK_SHEET_ENTRY_TYPE_GTK_TEXT_VIEW:
1601 return (GTK_TYPE_TEXT_VIEW);
1602
1603 case GTK_SHEET_ENTRY_TYPE_GTK_DATA_TEXT_VIEW:
1604 return (GTK_TYPE_DATA_TEXT_VIEW);
1605
1606 case GTK_SHEET_ENTRY_TYPE_GTK_SPIN_BUTTON:
1607 return (GTK_TYPE_SPIN_BUTTON);
1608
1609 case GTK_SHEET_ENTRY_TYPE_GTK_COMBO_BOX:
1610 return (GTK_TYPE_COMBO_BOX);
1611
1612 #if !GTK_CHECK_VERSION(2,24,0)
1613 case GTK_SHEET_ENTRY_TYPE_GTK_COMBO_BOX_ENTRY:
1614 return (GTK_TYPE_COMBO_BOX_ENTRY);
1615 #endif
1616
1617 #if !GTK_CHECK_VERSION(2,4,0)
1618 case GTK_SHEET_ENTRY_TYPE_GTK_COMBO:
1619 return (GTK_TYPE_COMBO);
1620 #endif
1621
1622 default:
1623 break;
1624 }
1625 return (G_TYPE_NONE);
1626 }
1627
1628 /*
1629 * gtk_sheet_set_property - set sheet property
1630 * gtk_sheet_get_property - get sheet property
1631 * gtk_sheet_class_init_properties - initialize class properties
1632 * gtk_sheet_class_init_signals - initialize class signals
1633 * gtk_sheet_class_init_bindings - initialize class key bindings
1634 */
1635
1636 static void
gtk_sheet_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1637 gtk_sheet_set_property(GObject *object,
1638 guint property_id,
1639 const GValue *value,
1640 GParamSpec *pspec)
1641 {
1642 GtkSheet *sheet = GTK_SHEET(object);
1643
1644 #if GTK_SHEET_DEBUG_PROPERTIES > 0
1645 g_debug("gtk_sheet_set_property: %s", pspec->name);
1646 #endif
1647
1648 switch(property_id)
1649 {
1650 case PROP_GTK_SHEET_TITLE:
1651 gtk_sheet_set_title(sheet, g_value_get_string(value));
1652 break;
1653
1654 case PROP_GTK_SHEET_DESCRIPTION:
1655 gtk_sheet_set_description(sheet, g_value_get_string(value));
1656 break;
1657
1658 case PROP_GTK_SHEET_NROWS:
1659 {
1660 gint newval = g_value_get_int(value);
1661
1662 if (newval < 0)
1663 break;
1664
1665 #if GTK_SHEET_DEBUG_PROPERTIES > 0
1666 g_debug("gtk_sheet_set_property: newval = %d sheet->maxrow %d", newval, sheet->maxrow);
1667 #endif
1668 if (newval < (sheet->maxrow + 1))
1669 {
1670 gtk_sheet_delete_rows(sheet, newval, (sheet->maxrow + 1) - newval);
1671 _gtk_sheet_recalc_view_range(sheet);
1672 }
1673 else if (newval > (sheet->maxrow + 1))
1674 {
1675 gtk_sheet_add_row(sheet, newval - (sheet->maxrow + 1));
1676 _gtk_sheet_recalc_view_range(sheet);
1677 }
1678 }
1679 break;
1680
1681 case PROP_GTK_SHEET_NCOLS:
1682 {
1683 gint newval = g_value_get_int(value);
1684
1685 if (newval < 0)
1686 break;
1687 #if GTK_SHEET_DEBUG_PROPERTIES > 0
1688 g_debug("gtk_sheet_set_property: newval = %d sheet->maxcol %d", newval, sheet->maxcol);
1689 #endif
1690 if (newval < (sheet->maxcol + 1))
1691 {
1692 gtk_sheet_delete_columns(sheet, newval, (sheet->maxcol + 1) - newval);
1693 _gtk_sheet_recalc_view_range(sheet);
1694 }
1695 else if (newval > (sheet->maxcol + 1))
1696 {
1697 gtk_sheet_add_column(sheet, newval - (sheet->maxcol + 1));
1698 _gtk_sheet_recalc_view_range(sheet);
1699 }
1700 }
1701 break;
1702
1703 case PROP_GTK_SHEET_LOCKED:
1704 gtk_sheet_set_locked(sheet, g_value_get_boolean(value));
1705 break;
1706
1707 case PROP_GTK_SHEET_SELECTION_MODE:
1708 gtk_sheet_set_selection_mode(sheet, g_value_get_enum(value));
1709 break;
1710
1711 case PROP_GTK_SHEET_AUTO_RESIZE:
1712 gtk_sheet_set_autoresize(sheet, g_value_get_boolean(value));
1713 break;
1714
1715 case PROP_GTK_SHEET_AUTO_RESIZE_ROWS:
1716 gtk_sheet_set_autoresize_rows(sheet, g_value_get_boolean(value));
1717 break;
1718
1719 case PROP_GTK_SHEET_AUTO_RESIZE_COLUMNS:
1720 gtk_sheet_set_autoresize_columns(sheet, g_value_get_boolean(value));
1721 break;
1722
1723 case PROP_GTK_SHEET_AUTO_SCROLL:
1724 gtk_sheet_set_autoscroll(sheet, g_value_get_boolean(value));
1725 break;
1726
1727 case PROP_GTK_SHEET_CLIP_TEXT:
1728 gtk_sheet_set_clip_text(sheet, g_value_get_boolean(value));
1729 break;
1730
1731 case PROP_GTK_SHEET_JUSTIFY_ENTRY:
1732 gtk_sheet_set_justify_entry(sheet, g_value_get_boolean(value));
1733 break;
1734
1735 case PROP_GTK_SHEET_BG_COLOR:
1736 gtk_sheet_set_background(sheet, g_value_get_boxed(value));
1737 break;
1738
1739 case PROP_GTK_SHEET_GRID_VISIBLE:
1740 gtk_sheet_show_grid(sheet, g_value_get_boolean(value));
1741 break;
1742
1743 case PROP_GTK_SHEET_GRID_COLOR:
1744 gtk_sheet_set_grid(sheet, g_value_get_boxed(value));
1745 break;
1746
1747 case PROP_GTK_SHEET_COLUMN_TITLES_VISIBLE:
1748 if (g_value_get_boolean(value))
1749 gtk_sheet_show_column_titles(sheet);
1750 else
1751 gtk_sheet_hide_column_titles(sheet);
1752 break;
1753
1754 case PROP_GTK_SHEET_COLUMNS_RESIZABLE:
1755 gtk_sheet_columns_set_resizable(sheet, g_value_get_boolean(value));
1756 break;
1757
1758 case PROP_GTK_SHEET_COLUMN_TITLES_HEIGHT:
1759 gtk_sheet_set_column_titles_height(sheet, g_value_get_uint(value));
1760 break;
1761
1762 case PROP_GTK_SHEET_ROW_TITLES_VISIBLE:
1763 if (g_value_get_boolean(value))
1764 gtk_sheet_show_row_titles(sheet);
1765 else
1766 gtk_sheet_hide_row_titles(sheet);
1767 break;
1768
1769 case PROP_GTK_SHEET_ROWS_RESIZABLE:
1770 gtk_sheet_rows_set_resizable(sheet, g_value_get_boolean(value));
1771 break;
1772
1773 case PROP_GTK_SHEET_ROW_TITLES_WIDTH:
1774 gtk_sheet_set_row_titles_width(sheet, g_value_get_uint(value));
1775 break;
1776
1777 case PROP_GTK_SHEET_ENTRY_TYPE:
1778 {
1779 GType entry_type = _gtk_sheet_entry_type_to_gtype(g_value_get_enum(value));
1780
1781 sheet->entry_type = entry_type; /* wanted entry type */
1782 gtk_sheet_change_entry(sheet, entry_type);
1783 }
1784 break;
1785
1786 case PROP_GTK_SHEET_VJUST:
1787 gtk_sheet_set_vjustification(sheet, g_value_get_enum(value));
1788 break;
1789
1790 case PROP_GTK_SHEET_TRAVERSE_TYPE:
1791 gtk_sheet_set_traverse_type(sheet, g_value_get_enum(value));
1792 break;
1793
1794 default:
1795 /* We don't have any other property... */
1796 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
1797 break;
1798 }
1799 _gtk_sheet_range_draw(sheet, NULL, TRUE);
1800
1801 /* this one will not work, it simply does noop
1802 gtk_widget_queue_draw(GTK_WIDGET(sheet));*/
1803 }
1804
1805 static void
gtk_sheet_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1806 gtk_sheet_get_property(GObject *object,
1807 guint property_id,
1808 GValue *value,
1809 GParamSpec *pspec)
1810 {
1811 GtkSheet *sheet = GTK_SHEET(object);
1812
1813 switch(property_id)
1814 {
1815 case PROP_GTK_SHEET_TITLE:
1816 g_value_set_string(value, sheet->title);
1817 break;
1818
1819 case PROP_GTK_SHEET_DESCRIPTION:
1820 g_value_set_string(value, sheet->description);
1821 break;
1822
1823 case PROP_GTK_SHEET_NROWS:
1824 g_value_set_int(value, sheet->maxrow + 1);
1825 break;
1826
1827 case PROP_GTK_SHEET_NCOLS:
1828 g_value_set_int(value, sheet->maxcol + 1);
1829 break;
1830
1831 case PROP_GTK_SHEET_LOCKED:
1832 g_value_set_boolean(value, sheet->locked);
1833 break;
1834
1835 case PROP_GTK_SHEET_SELECTION_MODE:
1836 g_value_set_enum(value, sheet->selection_mode);
1837 break;
1838
1839 case PROP_GTK_SHEET_AUTO_RESIZE:
1840 g_value_set_boolean(value, gtk_sheet_autoresize(sheet));
1841 break;
1842
1843 case PROP_GTK_SHEET_AUTO_RESIZE_ROWS:
1844 g_value_set_boolean(value, gtk_sheet_autoresize_rows(sheet));
1845 break;
1846
1847 case PROP_GTK_SHEET_AUTO_RESIZE_COLUMNS:
1848 g_value_set_boolean(value, gtk_sheet_autoresize_columns(sheet));
1849 break;
1850
1851 case PROP_GTK_SHEET_AUTO_SCROLL:
1852 g_value_set_boolean(value, sheet->autoscroll);
1853 break;
1854
1855 case PROP_GTK_SHEET_CLIP_TEXT:
1856 g_value_set_boolean(value, sheet->clip_text);
1857 break;
1858
1859 case PROP_GTK_SHEET_JUSTIFY_ENTRY:
1860 g_value_set_boolean(value, sheet->justify_entry);
1861 break;
1862
1863 case PROP_GTK_SHEET_BG_COLOR:
1864 g_value_set_boxed(value, &sheet->bg_color);
1865 break;
1866
1867 case PROP_GTK_SHEET_GRID_VISIBLE:
1868 g_value_set_boolean(value, sheet->show_grid);
1869 break;
1870
1871 case PROP_GTK_SHEET_GRID_COLOR:
1872 g_value_set_boxed(value, &sheet->grid_color);
1873 break;
1874
1875 case PROP_GTK_SHEET_COLUMN_TITLES_VISIBLE:
1876 g_value_set_boolean(value, sheet->column_titles_visible);
1877 break;
1878
1879 case PROP_GTK_SHEET_COLUMNS_RESIZABLE:
1880 g_value_set_boolean(value, sheet->columns_resizable);
1881 break;
1882
1883 case PROP_GTK_SHEET_COLUMN_TITLES_HEIGHT:
1884 g_value_set_uint(value, sheet->column_title_area.height);
1885 break;
1886
1887 case PROP_GTK_SHEET_ROW_TITLES_VISIBLE:
1888 g_value_set_boolean(value, sheet->row_titles_visible);
1889 break;
1890
1891 case PROP_GTK_SHEET_ROWS_RESIZABLE:
1892 g_value_set_boolean(value, sheet->rows_resizable);
1893 break;
1894
1895 case PROP_GTK_SHEET_ROW_TITLES_WIDTH:
1896 g_value_set_uint(value, sheet->row_title_area.width);
1897 break;
1898
1899 case PROP_GTK_SHEET_ENTRY_TYPE:
1900 g_value_set_enum(value, _gtk_sheet_entry_type_from_gtype(sheet->entry_type));
1901 break;
1902
1903 case PROP_GTK_SHEET_VJUST:
1904 g_value_set_enum(value, sheet->vjust);
1905 break;
1906
1907 case PROP_GTK_SHEET_TRAVERSE_TYPE:
1908 g_value_set_enum(value, sheet->traverse_type);
1909 break;
1910
1911 default:
1912 /* We don't have any other property... */
1913 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
1914 break;
1915 }
1916 }
1917
1918 static void
gtk_sheet_class_init_properties(GObjectClass * gobject_class)1919 gtk_sheet_class_init_properties(GObjectClass *gobject_class)
1920 {
1921 GParamSpec *pspec;
1922
1923 gobject_class->set_property = gtk_sheet_set_property;
1924 gobject_class->get_property = gtk_sheet_get_property;
1925
1926 /**
1927 * GtkSheet:title:
1928 *
1929 * The sheets title string
1930 */
1931 pspec = g_param_spec_string("title", "Sheet title",
1932 "The sheets title string",
1933 "GtkSheet" /* default value */,
1934 G_PARAM_READWRITE);
1935 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_TITLE, pspec);
1936
1937 /**
1938 * GtkSheet:description:
1939 *
1940 * The sheets description, a place to store further information
1941 * for application use
1942 *
1943 */
1944 pspec = g_param_spec_string("description", "Sheet description",
1945 "The sheets description and further information for application use",
1946 "" /* default value */,
1947 G_PARAM_READWRITE);
1948 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_DESCRIPTION, pspec);
1949
1950 #if 0
1951 /**
1952 * GtkSheet:n-cols:
1953 *
1954 * Number of columns in the sheet
1955 */
1956 pspec = g_param_spec_int ("n-cols", "Number of columns",
1957 "Number of columns in the sheet",
1958 0, 1024, 0,
1959 G_PARAM_READWRITE);
1960 g_object_class_install_property (gobject_class, PROP_GTK_SHEET_NCOLS, pspec);
1961 #endif
1962
1963 /**
1964 * GtkSheet:n-rows:
1965 *
1966 * Number of rows in the sheet
1967 */
1968 pspec = g_param_spec_int("n-rows", "Number of rows",
1969 "Number of rows in the sheet",
1970 0, 1000000, 0,
1971 G_PARAM_READWRITE);
1972 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_NROWS, pspec);
1973
1974 /**
1975 * GtkSheet:locked:
1976 *
1977 * If the sheet ist locked, it is not editable, cell contents
1978 * cannot be modified by the user.
1979 */
1980 pspec = g_param_spec_boolean("locked", "Locked",
1981 "If the sheet is locked, it is not editable, cell contents cannot be modified by the user",
1982 FALSE,
1983 G_PARAM_READWRITE);
1984 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_LOCKED, pspec);
1985
1986 /**
1987 * GtkSheet:selection-mode:
1988 *
1989 * Sets the selection mode of the cells in a #GtkSheet
1990 */
1991 pspec = g_param_spec_enum("selection-mode", "Selection mode",
1992 "Sets the selection mode of the cells in a sheet",
1993 gtk_selection_mode_get_type(),
1994 GTK_SELECTION_BROWSE,
1995 G_PARAM_READWRITE);
1996 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_SELECTION_MODE, pspec);
1997
1998 #if 1
1999 /**
2000 * GtkSheet:autoresize:
2001 *
2002 * Autoreisize cells while typing (rows and columns)
2003 *
2004 * deprecated: 3.0: use autoresize-rows, autoresize-columns
2005 * instead
2006 */
2007 pspec = g_param_spec_boolean("autoresize", "Autoresize cells",
2008 "Autoreisize rows and columns while typing",
2009 FALSE,
2010 G_PARAM_READWRITE);
2011 g_object_class_install_property(gobject_class,
2012 PROP_GTK_SHEET_AUTO_RESIZE, pspec);
2013 #endif
2014
2015 /**
2016 * GtkSheet:autoresize-rows:
2017 *
2018 * Autoreisize rows while typing
2019 */
2020 pspec = g_param_spec_boolean("autoresize-rows", "Autoresize rows",
2021 "Autoreisize rows while typing",
2022 FALSE,
2023 G_PARAM_READWRITE);
2024 g_object_class_install_property(gobject_class,
2025 PROP_GTK_SHEET_AUTO_RESIZE_ROWS, pspec);
2026
2027 /**
2028 * GtkSheet:autoresize-cols:
2029 *
2030 * Autoreisize columns while typing
2031 */
2032 pspec = g_param_spec_boolean("autoresize-cols", "Autoresize cols",
2033 "Autoreisize columns while typing",
2034 FALSE,
2035 G_PARAM_READWRITE);
2036 g_object_class_install_property(gobject_class,
2037 PROP_GTK_SHEET_AUTO_RESIZE_COLUMNS, pspec);
2038
2039 /**
2040 * GtkSheet:autoscroll:
2041 *
2042 * The sheet will be automatically scrolled when you move beyond
2043 * the last visible row/column
2044 */
2045 pspec = g_param_spec_boolean("autoscroll", "Autoscroll sheet",
2046 "The sheet will be automatically scrolled when you move beyond the last row/column",
2047 TRUE,
2048 G_PARAM_READWRITE);
2049 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_AUTO_SCROLL, pspec);
2050
2051 /**
2052 * GtkSheet:clip-text:
2053 *
2054 * Clip text in cells
2055 */
2056 pspec = g_param_spec_boolean("clip-text", "Clip cell text",
2057 "Clip text in cells",
2058 FALSE,
2059 G_PARAM_READWRITE);
2060 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_CLIP_TEXT, pspec);
2061
2062 pspec = g_param_spec_boolean("justify-entry", "Justify cell entry",
2063 "Adapt cell entry editor to the cell justification",
2064 TRUE,
2065 G_PARAM_READWRITE);
2066 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_JUSTIFY_ENTRY, pspec);
2067
2068 /**
2069 * GtkSheet:bgcolor:
2070 *
2071 * Background color of the sheet
2072 */
2073 pspec = g_param_spec_boxed("bgcolor", "Background color",
2074 "Background color of the sheet",
2075 GDK_TYPE_COLOR,
2076 G_PARAM_READWRITE);
2077 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_BG_COLOR, pspec);
2078
2079 /**
2080 * GtkSheet:grid-visible:
2081 *
2082 * Sets the visibility of grid
2083 */
2084 pspec = g_param_spec_boolean("grid-visible", "Grid visible",
2085 "Sets the visibility of grid",
2086 TRUE,
2087 G_PARAM_READWRITE);
2088 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_GRID_VISIBLE, pspec);
2089
2090 /**
2091 * GtkSheet:grid-color:
2092 *
2093 * Color of the grid
2094 */
2095 pspec = g_param_spec_boxed("grid-color", "Grid color",
2096 "Color of the grid",
2097 GDK_TYPE_COLOR,
2098 G_PARAM_READWRITE);
2099 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_GRID_COLOR, pspec);
2100
2101 /**
2102 * GtkSheet:col-titles-visible:
2103 *
2104 * Visibility of the column titles
2105 */
2106 pspec = g_param_spec_boolean("col-titles-visible", "Column titles visible",
2107 "Visibility of the column titles",
2108 TRUE,
2109 G_PARAM_READWRITE);
2110 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_COLUMN_TITLES_VISIBLE, pspec);
2111
2112 /**
2113 * GtkSheet:columns-resizable:
2114 *
2115 * Columns resizable
2116 */
2117 pspec = g_param_spec_boolean("columns-resizable", "Columns resizable",
2118 "Columns resizable",
2119 TRUE,
2120 G_PARAM_READWRITE);
2121 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_COLUMNS_RESIZABLE, pspec);
2122
2123 /**
2124 * GtkSheet:col-titles-height:
2125 *
2126 * Height of the column titles
2127 */
2128 pspec = g_param_spec_uint("col-titles-height", "Column titles height",
2129 "Height of the column title area",
2130 0, 1024, GTK_SHEET_ROW_DEFAULT_HEIGHT,
2131 G_PARAM_READWRITE);
2132 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_COLUMN_TITLES_HEIGHT, pspec);
2133
2134 /**
2135 * GtkSheet:row-titles-visible:
2136 *
2137 * Row titles visible
2138 */
2139 pspec = g_param_spec_boolean("row-titles-visible", "Row titles visible",
2140 "Row titles visible",
2141 TRUE,
2142 G_PARAM_READWRITE);
2143 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_ROW_TITLES_VISIBLE, pspec);
2144
2145 /**
2146 * GtkSheet:rows-resizable:
2147 *
2148 * Rows resizable
2149 */
2150 pspec = g_param_spec_boolean("rows-resizable", "Rows resizable",
2151 "Rows resizable",
2152 TRUE,
2153 G_PARAM_READWRITE);
2154 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_ROWS_RESIZABLE, pspec);
2155
2156 /**
2157 * GtkSheet:row-titles-width:
2158 *
2159 * Width of the row title area
2160 */
2161 pspec = g_param_spec_uint("row-titles-width", "Row titles width",
2162 "Width of the row title area",
2163 0, 2048, GTK_SHEET_COLUMN_DEFAULT_WIDTH,
2164 G_PARAM_READWRITE);
2165 g_object_class_install_property(gobject_class, PROP_GTK_SHEET_ROW_TITLES_WIDTH, pspec);
2166
2167 /**
2168 * GtkSheet:entry-type:
2169 *
2170 * Sheet cell entry widget type
2171 */
2172 pspec = g_param_spec_enum("entry-type", "Entry Type",
2173 "Sheet entry type, if not default",
2174 gtk_sheet_entry_type_get_type(),
2175 GTK_SHEET_ENTRY_TYPE_DEFAULT,
2176 G_PARAM_READWRITE);
2177 g_object_class_install_property(gobject_class,
2178 PROP_GTK_SHEET_ENTRY_TYPE, pspec);
2179
2180 /**
2181 * GtkSheet:vjust:
2182 *
2183 * Default vertical cell text justification
2184 */
2185 pspec = g_param_spec_enum("vjust", "Vertical justification",
2186 "Default sheet vertical cell text justification",
2187 gtk_sheet_vertical_justification_get_type(),
2188 GTK_SHEET_VERTICAL_JUSTIFICATION_TOP,
2189 G_PARAM_READWRITE);
2190 g_object_class_install_property(gobject_class,
2191 PROP_GTK_SHEET_VJUST, pspec);
2192
2193 /**
2194 * GtkSheet:traverse_type:
2195 *
2196 * Default traverse all cells
2197 */
2198 pspec = g_param_spec_enum("traverse-type", "Traversal type",
2199 "Default sheet traversal type",
2200 gtk_sheet_traverse_type_get_type(),
2201 GTK_SHEET_TRAVERSE_ALL,
2202 G_PARAM_READWRITE);
2203 g_object_class_install_property(gobject_class,
2204 PROP_GTK_SHEET_TRAVERSE_TYPE, pspec);
2205 }
2206
2207 #if GTK_SHEET_DEBUG_SIGNALS > 0
2208
gtk_sheet_debug_select_row(GtkSheet * sheet,gint row)2209 static void gtk_sheet_debug_select_row(GtkSheet *sheet, gint row)
2210 {
2211 g_debug("SIGNAL select-row %p row %d", sheet, row);
2212 }
2213
gtk_sheet_debug_select_column(GtkSheet * sheet,gint column)2214 static void gtk_sheet_debug_select_column(GtkSheet *sheet, gint column)
2215 {
2216 g_debug("SIGNAL select-column %p col %d", sheet, column);
2217 }
2218
gtk_sheet_debug_select_range(GtkSheet * sheet,GtkSheetRange * range)2219 static void gtk_sheet_debug_select_range(GtkSheet *sheet, GtkSheetRange *range)
2220 {
2221 g_debug("SIGNAL select-range %p {%d, %d, %d, %d}", sheet,
2222 range->row0, range->col0, range->rowi, range->coli);
2223 }
2224
gtk_sheet_debug_clip_range(GtkSheet * sheet,GtkSheetRange * range)2225 static void gtk_sheet_debug_clip_range(GtkSheet *sheet, GtkSheetRange *range)
2226 {
2227 g_debug("SIGNAL clip-range %p {%d, %d, %d, %d}", sheet,
2228 range->row0, range->col0, range->rowi, range->coli);
2229 }
2230
gtk_sheet_debug_resize_range(GtkSheet * sheet,GtkSheetRange * old_range,GtkSheetRange * new_range)2231 static void gtk_sheet_debug_resize_range(GtkSheet *sheet,
2232 GtkSheetRange *old_range, GtkSheetRange *new_range)
2233 {
2234 g_debug("SIGNAL resize-range %p {%d, %d, %d, %d} -> {%d, %d, %d, %d}", sheet,
2235 old_range->row0, old_range->col0, old_range->rowi, old_range->coli,
2236 new_range->row0, new_range->col0, new_range->rowi, new_range->coli);
2237 }
2238
gtk_sheet_debug_move_range(GtkSheet * sheet,GtkSheetRange * old_range,GtkSheetRange * new_range)2239 static void gtk_sheet_debug_move_range(GtkSheet *sheet,
2240 GtkSheetRange *old_range, GtkSheetRange *new_range)
2241 {
2242 g_debug("SIGNAL move-range %p {%d, %d, %d, %d} -> {%d, %d, %d, %d}", sheet,
2243 old_range->row0, old_range->col0, old_range->rowi, old_range->coli,
2244 new_range->row0, new_range->col0, new_range->rowi, new_range->coli);
2245 }
2246
gtk_sheet_debug_traverse(GtkSheet * sheet,gint row,gint column,gint * new_row,gint * new_column)2247 static gboolean gtk_sheet_debug_traverse(GtkSheet *sheet,
2248 gint row, gint column, gint *new_row, gint *new_column)
2249 {
2250 g_debug("SIGNAL traverse %p row %d col %d nrow %d ncol %d", sheet,
2251 row, column, *new_row, *new_column);
2252 return (TRUE);
2253 }
2254
gtk_sheet_debug_deactivate(GtkSheet * sheet,gint row,gint column)2255 static gboolean gtk_sheet_debug_deactivate(GtkSheet *sheet, gint row, gint column)
2256 {
2257 g_debug("SIGNAL deactivate %p row %d col %d", sheet, row, column);
2258 return (TRUE);
2259 }
2260
gtk_sheet_debug_activate(GtkSheet * sheet,gint row,gint column)2261 static gboolean gtk_sheet_debug_activate(GtkSheet *sheet, gint row, gint column)
2262 {
2263 g_debug("SIGNAL activate %p row %d col %d", sheet, row, column);
2264 return (TRUE);
2265 }
2266
gtk_sheet_debug_set_cell(GtkSheet * sheet,gint row,gint column)2267 static void gtk_sheet_debug_set_cell(GtkSheet *sheet, gint row, gint column)
2268 {
2269 g_debug("SIGNAL set-cell %p row %d col %d", sheet, row, column);
2270 }
2271
gtk_sheet_debug_clear_cell(GtkSheet * sheet,gint row,gint column)2272 static void gtk_sheet_debug_clear_cell(GtkSheet *sheet, gint row, gint column)
2273 {
2274 g_debug("SIGNAL clear-cell %p row %d col %d", sheet, row, column);
2275 }
2276
2277 # if 0
gtk_sheet_debug_changed(GtkSheet * sheet,gint row,gint column)2278 static void gtk_sheet_debug_changed(GtkSheet *sheet, gint row, gint column)
2279 {
2280 g_debug("SIGNAL changed %p row %d col %d", sheet, row, column);
2281 }
2282 # endif
2283
gtk_sheet_debug_new_column_width(GtkSheet * sheet,gint col,guint width)2284 static void gtk_sheet_debug_new_column_width(GtkSheet *sheet, gint col, guint width)
2285 {
2286 g_debug("SIGNAL new-column-width %p col %d width %d", sheet, col, width);
2287 }
2288
gtk_sheet_debug_new_row_height(GtkSheet * sheet,gint row,guint height)2289 static void gtk_sheet_debug_new_row_height(GtkSheet *sheet, gint row, guint height)
2290 {
2291 g_debug("SIGNAL new-row-height %p row %d height %d", sheet, row, height);
2292 }
2293
gtk_sheet_debug_focus_in_event(GtkSheet * sheet,GdkEventFocus * event)2294 static gboolean gtk_sheet_debug_focus_in_event(GtkSheet *sheet, GdkEventFocus *event)
2295 {
2296 g_debug("SIGNAL focus-in-event %p event %p", sheet, event);
2297 return (FALSE);
2298 }
2299
gtk_sheet_debug_focus_out_event(GtkSheet * sheet,GdkEventFocus * event)2300 static gboolean gtk_sheet_debug_focus_out_event(GtkSheet *sheet, GdkEventFocus *event)
2301 {
2302 g_debug("SIGNAL focus-out-event %p event %p", sheet, event);
2303 return (FALSE);
2304 }
2305
2306 #endif
2307
2308 static void
_gtk_sheet_class_init_signals(GtkObjectClass * object_class,GtkWidgetClass * widget_class)2309 _gtk_sheet_class_init_signals(GtkObjectClass *object_class,
2310 GtkWidgetClass *widget_class)
2311 {
2312 /**
2313 * GtkSheet::select-row:
2314 * @sheet: the sheet widget that emitted the signal
2315 * @row: the newly selected row index
2316 *
2317 * Emmited when a row has been selected.
2318 */
2319 sheet_signals[SELECT_ROW] =
2320 g_signal_new("select-row",
2321 G_TYPE_FROM_CLASS(object_class),
2322 G_SIGNAL_RUN_LAST,
2323 G_STRUCT_OFFSET(GtkSheetClass, select_row),
2324 NULL, NULL,
2325 gtkextra_VOID__INT,
2326 G_TYPE_NONE, 1, G_TYPE_INT);
2327
2328 /**
2329 * GtkSheet::select-column:
2330 * @sheet: the sheet widget that emitted the signal
2331 * @select_column: the newly selected column index
2332 *
2333 * Emmited when a column has been selected.
2334 */
2335 sheet_signals[SELECT_COLUMN] =
2336 g_signal_new("select-column",
2337 G_TYPE_FROM_CLASS(object_class),
2338 G_SIGNAL_RUN_LAST,
2339 G_STRUCT_OFFSET(GtkSheetClass, select_column),
2340 NULL, NULL,
2341 gtkextra_VOID__INT,
2342 G_TYPE_NONE, 1, G_TYPE_INT);
2343
2344 /**
2345 * GtkSheet::select-range:
2346 * @sheet: the sheet widget that emitted the signal
2347 * @select_range: the newly selected #GtkSheetRange
2348 *
2349 * Emmited when a #GtkSheetRange has been selected.
2350 */
2351 sheet_signals[SELECT_RANGE] =
2352 g_signal_new("select-range",
2353 G_TYPE_FROM_CLASS(object_class),
2354 G_SIGNAL_RUN_LAST,
2355 G_STRUCT_OFFSET(GtkSheetClass, select_range),
2356 NULL, NULL,
2357 gtkextra_VOID__BOXED,
2358 G_TYPE_NONE, 1, G_TYPE_SHEET_RANGE);
2359
2360 /**
2361 * GtkSheet::clip-range:
2362 * @sheet: the sheet widget that emitted the signal
2363 * @clip_range: the newly selected #GtkSheetRange
2364 *
2365 * Emmited when a #GtkSheetRange is clipping.
2366 */
2367 sheet_signals[CLIP_RANGE] =
2368 g_signal_new("clip-range",
2369 G_TYPE_FROM_CLASS(object_class),
2370 G_SIGNAL_RUN_LAST,
2371 G_STRUCT_OFFSET(GtkSheetClass, clip_range),
2372 NULL, NULL,
2373 gtkextra_VOID__BOXED,
2374 G_TYPE_NONE, 1, G_TYPE_SHEET_RANGE);
2375
2376 /**
2377 * GtkSheet::resize-range:
2378 * @sheet: the sheet widget that emitted the signal
2379 * @old_range: the previous selected #GtkSheetRange.
2380 * @new_range: the newly selected #GtkSheetRange.
2381 *
2382 * Emmited when a #GtkSheetRange is resized.
2383 */
2384 sheet_signals[RESIZE_RANGE] =
2385 g_signal_new("resize-range",
2386 G_TYPE_FROM_CLASS(object_class),
2387 G_SIGNAL_RUN_LAST,
2388 G_STRUCT_OFFSET(GtkSheetClass, resize_range),
2389 NULL, NULL,
2390 gtkextra_VOID__BOXED_BOXED,
2391 G_TYPE_NONE, 2, G_TYPE_SHEET_RANGE, G_TYPE_SHEET_RANGE);
2392
2393 /**
2394 * GtkSheet::move-range:
2395 * @sheet: the sheet widget that emitted the signal.
2396 * @old_range: the previous selected #GtkSheetRange.
2397 * @new_range: the newly selected #GtkSheetRange.
2398 *
2399 * Emmited when a #GtkSheetRange is moved.
2400 */
2401 sheet_signals[MOVE_RANGE] =
2402 g_signal_new("move-range",
2403 G_TYPE_FROM_CLASS(object_class),
2404 G_SIGNAL_RUN_LAST,
2405 G_STRUCT_OFFSET(GtkSheetClass, move_range),
2406 NULL, NULL,
2407 gtkextra_VOID__BOXED_BOXED,
2408 G_TYPE_NONE, 2, G_TYPE_SHEET_RANGE, G_TYPE_SHEET_RANGE);
2409
2410 /**
2411 * GtkSheet::traverse:
2412 * @sheet: the sheet widget that emitted the signal.
2413 * @row: row number of old cell
2414 * @column: column number of old cell
2415 * @*new_row: row number of target cell, changeable
2416 * @*new_column: column number of target cell, changeable
2417 *
2418 * The "traverse" is emited before "deactivate" and allows to
2419 * veto the movement. In such case, the entry will remain in the
2420 * site and the other signals will not be emited.
2421 *
2422 * The signal handler must return TRUE to allow the movement,
2423 * FALSE to veto the movement.
2424 */
2425 sheet_signals[TRAVERSE] =
2426 g_signal_new("traverse",
2427 G_TYPE_FROM_CLASS(object_class),
2428 G_SIGNAL_RUN_LAST,
2429 G_STRUCT_OFFSET(GtkSheetClass, traverse),
2430 NULL, NULL,
2431 gtkextra_BOOLEAN__INT_INT_POINTER_POINTER,
2432 G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT,
2433 G_TYPE_POINTER, G_TYPE_POINTER);
2434
2435 /**
2436 * GtkSheet::deactivate:
2437 * @sheet: the sheet widget that emitted the signal
2438 * @row: row number of deactivated cell.
2439 * @column: column number of deactivated cell.
2440 *
2441 * Emmited whenever a cell is deactivated(you click on other
2442 * cell or start a new selection).
2443 *
2444 * The signal handler must return TRUE in order to allow
2445 * deactivation, FALSE to deny deactivation.
2446 */
2447 sheet_signals[DEACTIVATE] =
2448 g_signal_new("deactivate",
2449 G_TYPE_FROM_CLASS(object_class),
2450 G_SIGNAL_RUN_LAST,
2451 G_STRUCT_OFFSET(GtkSheetClass, deactivate),
2452 NULL, NULL,
2453 gtkextra_BOOLEAN__INT_INT,
2454 G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT);
2455
2456 /**
2457 * GtkSheet::activate:
2458 * @sheet: the sheet widget that emitted the signal
2459 * @row: row number of activated cell.
2460 * @column: column number of activated cell.
2461 *
2462 * Emmited whenever a cell is activated(you click on it).
2463 *
2464 * FIXME:: The return value is ignored (not yet implemented).
2465 */
2466 sheet_signals[ACTIVATE] =
2467 g_signal_new("activate",
2468 G_TYPE_FROM_CLASS(object_class),
2469 G_SIGNAL_RUN_LAST,
2470 G_STRUCT_OFFSET(GtkSheetClass, activate),
2471 NULL, NULL,
2472 gtkextra_BOOLEAN__INT_INT,
2473 G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT);
2474
2475 /**
2476 * GtkSheet::set-cell:
2477 * @sheet: the sheet widget that emitted the signal
2478 * @row: row number of activated cell.
2479 * @column: column number of activated cell.
2480 *
2481 * Emited when clicking on a non-empty cell.
2482 */
2483 sheet_signals[SET_CELL] =
2484 g_signal_new("set-cell",
2485 G_TYPE_FROM_CLASS(object_class),
2486 G_SIGNAL_RUN_LAST,
2487 G_STRUCT_OFFSET(GtkSheetClass, set_cell),
2488 NULL, NULL,
2489 gtkextra_VOID__INT_INT,
2490 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
2491
2492 /**
2493 * GtkSheet::clear-cell:
2494 * @sheet: the sheet widget that emitted the signal
2495 * @row: row number of cleared cell.
2496 * @column: column number of cleared cell.
2497 *
2498 * Emited when when the content of the cell is erased.
2499 */
2500 sheet_signals[CLEAR_CELL] =
2501 g_signal_new("clear-cell",
2502 G_TYPE_FROM_CLASS(object_class),
2503 G_SIGNAL_RUN_LAST,
2504 G_STRUCT_OFFSET(GtkSheetClass, clear_cell),
2505 NULL, NULL,
2506 gtkextra_VOID__INT_INT,
2507 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
2508
2509 /**
2510 * GtkSheet::changed:
2511 * @sheet: the sheet widget that emitted the signal
2512 * @row: row number of changed cell.
2513 * @column: column number of changed cell.
2514 *
2515 * "Emited when typing into the active cell, changing its content.
2516 * It is emitted after each key press in cell and after deactivating cell.
2517 */
2518 sheet_signals[CHANGED] =
2519 g_signal_new("changed",
2520 G_TYPE_FROM_CLASS(object_class),
2521 G_SIGNAL_RUN_LAST,
2522 G_STRUCT_OFFSET(GtkSheetClass, changed),
2523 NULL, NULL,
2524 gtkextra_VOID__INT_INT,
2525 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
2526
2527 /**
2528 * GtkSheet::new-column-width:
2529 * @sheet: the sheet widget that emitted the signal
2530 * @col: modified column number.
2531 * @width: new column width
2532 *
2533 * Emited when the width of a column is modified.
2534 */
2535 sheet_signals[NEW_COL_WIDTH] =
2536 g_signal_new("new-column-width",
2537 G_TYPE_FROM_CLASS(object_class),
2538 G_SIGNAL_RUN_LAST,
2539 G_STRUCT_OFFSET(GtkSheetClass, changed),
2540 NULL, NULL,
2541 gtkextra_VOID__INT_INT,
2542 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
2543
2544 /**
2545 * GtkSheet::new-row-height:
2546 * @sheet: the sheet widget that emitted the signal
2547 * @row: modified row number.
2548 * @height: new row height.
2549 *
2550 * Emited when the height of a row is modified.
2551 */
2552 sheet_signals[NEW_ROW_HEIGHT] =
2553 g_signal_new("new-row-height",
2554 G_TYPE_FROM_CLASS(object_class),
2555 G_SIGNAL_RUN_LAST,
2556 G_STRUCT_OFFSET(GtkSheetClass, changed),
2557 NULL, NULL,
2558 gtkextra_VOID__INT_INT,
2559 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
2560
2561 /**
2562 * GtkSheet::entry-focus-in:
2563 * @sheet: the sheet widget that emitted the signal
2564 * @event: the #GdkEventFocus which triggered this signal
2565 *
2566 * The ::entry-focus-in signal will be emitted when the keyboard
2567 * focus enters the sheet_entry editor.
2568 *
2569 * Returns: %TRUE to stop other handlers from being invoked for the event.
2570 * %FALSE to propagate the event further.
2571 *
2572 * Since: 3.0.1
2573 */
2574 sheet_signals[ENTRY_FOCUS_IN] =
2575 g_signal_new("entry-focus-in",
2576 G_TYPE_FROM_CLASS(object_class),
2577 G_SIGNAL_RUN_LAST,
2578 G_STRUCT_OFFSET(GtkSheetClass, focus_in_event),
2579 NULL, NULL,
2580 gtkextra_BOOLEAN__BOXED,
2581 G_TYPE_BOOLEAN, 1,
2582 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2583
2584 /**
2585 * GtkSheet::entry-focus-out:
2586 * @sheet: the sheet widget that emitted the signal
2587 * @event: the #GdkEventFocus which triggered this signal
2588 *
2589 * The ::entry-focus-out signal will be emitted when the
2590 * keyboard focus leaves the sheet_entry editor.
2591 *
2592 * Returns: %TRUE to stop other handlers from being invoked for the event.
2593 * %FALSE to propagate the event further.
2594 *
2595 * Since: 3.0.1
2596 */
2597 sheet_signals[ENTRY_FOCUS_OUT] =
2598 g_signal_new("entry-focus-out",
2599 G_TYPE_FROM_CLASS(object_class),
2600 G_SIGNAL_RUN_LAST,
2601 G_STRUCT_OFFSET(GtkSheetClass, focus_out_event),
2602 NULL, NULL,
2603 gtkextra_BOOLEAN__BOXED,
2604 G_TYPE_BOOLEAN, 1,
2605 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2606
2607 /**
2608 * GtkSheet::populate-popup:
2609 * @sheet: the sheet widget that emitted the signal
2610 * @menu: the menu that ist being populated
2611 *
2612 * The ::populate-popup signal will be emitted when the user
2613 * activates the popup menu of the sheet_entry editor.
2614 *
2615 * The emission of this signal is only supported for #GtkEntry,
2616 * #GtkDataEntry, #GtkItemEntry and #GtkTextView.
2617 *
2618 * Since: 3.0.1
2619 */
2620 sheet_signals[ENTRY_POPULATE_POPUP] =
2621 g_signal_new("populate-popup",
2622 G_TYPE_FROM_CLASS(object_class),
2623 G_SIGNAL_RUN_LAST,
2624 0,
2625 NULL, NULL,
2626 gtkextra_VOID__OBJECT,
2627 G_TYPE_NONE, 1,
2628 gtk_menu_get_type());
2629
2630
2631 /**
2632 * GtkSheet::set-scroll-adjustments:
2633 * @sheet: the sheet widget that emitted the signal
2634 * @hadjustment: horizontal #GtkAdjustment.
2635 * @vadjustment: vertical #GtkAdkjustment.
2636 *
2637 * Emited when scroll adjustments are set.
2638 */
2639 widget_class->set_scroll_adjustments_signal =
2640 g_signal_new("set-scroll-adjustments",
2641 G_TYPE_FROM_CLASS(object_class),
2642 G_SIGNAL_RUN_LAST,
2643 G_STRUCT_OFFSET(GtkSheetClass, set_scroll_adjustments),
2644 NULL, NULL,
2645 gtkextra_VOID__OBJECT_OBJECT,
2646 G_TYPE_NONE, 2, gtk_adjustment_get_type(),
2647 gtk_adjustment_get_type());
2648
2649 /**
2650 * GtkSheet::move-cursor:
2651 * @sheet: the sheet widget that emitted the signal
2652 * @step: the granularity of the move, as a #GtkMovementStep
2653 * @count: the number of @step units to move
2654 * @extend_selection: %TRUE if the move should extend the selection
2655 *
2656 * The ::move-cursor signal is a keybinding signal which gets
2657 * emitted when the user initiates a cursor movement.
2658 *
2659 * Applications should not connect to it, but may emit it with
2660 * g_signal_emit_by_name() if they need to control the cursor
2661 * programmatically.
2662 *
2663 * Since: 3.0.2
2664 */
2665 sheet_signals[MOVE_CURSOR] =
2666 g_signal_new("move-cursor",
2667 G_TYPE_FROM_CLASS(object_class),
2668 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2669 G_STRUCT_OFFSET(GtkSheetClass, move_cursor),
2670 NULL, NULL,
2671 gtkextra_VOID__ENUM_INT_BOOLEAN,
2672 G_TYPE_NONE, 3,
2673 GTK_TYPE_MOVEMENT_STEP,
2674 G_TYPE_INT,
2675 G_TYPE_BOOLEAN);
2676
2677 /**
2678 * GtkSheet::enter-pressed:
2679 * @sheet: the sheet widget that emitted the signal
2680 * @event: (type Gdk.EventKey): the #GdkEventKey which triggered this signal.
2681 *
2682 * This signal intercepts RETURN and ENTER key-press-events
2683 * before they are processed by the sheet-entry editor. Any
2684 * modifier combinations on these keys may trigger the signal.
2685 *
2686 * The default behaviour of the sheet-entry editor is to move
2687 * the active cell, which might not be appropriate for the type
2688 * of application.
2689 *
2690 * Returns: %TRUE to block the sheet-entry from processing
2691 * the event. %FALSE to propagate the event to the
2692 * sheet-entry.
2693 */
2694 sheet_signals[ENTER_PRESSED] =
2695 g_signal_new("enter-pressed",
2696 G_TYPE_FROM_CLASS(object_class),
2697 G_SIGNAL_RUN_LAST,
2698 0,
2699 NULL, NULL,
2700 gtkextra_BOOLEAN__BOXED,
2701 G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2702
2703 }
2704
2705 static void
_add_binding_ext(GtkBindingSet * binding_set,gint key,gint alt_key,GdkModifierType mods,GtkMovementStep step,gint count,gboolean extend_selection)2706 _add_binding_ext(GtkBindingSet *binding_set,
2707 gint key, gint alt_key, GdkModifierType mods,
2708 GtkMovementStep step, gint count,
2709 gboolean extend_selection)
2710 {
2711 gtk_binding_entry_remove(binding_set, key, mods);
2712
2713 gtk_binding_entry_add_signal(binding_set,
2714 key, mods,
2715 "move-cursor", 3,
2716 G_TYPE_ENUM, step,
2717 G_TYPE_INT, count,
2718 G_TYPE_BOOLEAN, extend_selection);
2719 if (alt_key)
2720 {
2721 gtk_binding_entry_remove(binding_set, alt_key, mods);
2722
2723 gtk_binding_entry_add_signal(binding_set,
2724 alt_key, mods,
2725 "move-cursor", 3,
2726 G_TYPE_ENUM, step,
2727 G_TYPE_INT, count,
2728 G_TYPE_BOOLEAN, extend_selection);
2729 }
2730 }
2731
2732 static void
_add_mod_binding(GtkBindingSet * binding_set,gint key,gint alt_key,GdkModifierType mods,GtkMovementStep step,gint count)2733 _add_mod_binding(GtkBindingSet *binding_set,
2734 gint key, gint alt_key, GdkModifierType mods,
2735 GtkMovementStep step, gint count)
2736 {
2737 _add_binding_ext(binding_set,
2738 key, alt_key, GTK_SHEET_MOD_MASK | mods,
2739 step, count,
2740 FALSE);
2741
2742 _add_binding_ext(binding_set,
2743 key, alt_key, GTK_SHEET_MOD_MASK | GDK_SHIFT_MASK | mods,
2744 step, count,
2745 TRUE);
2746 }
2747
2748 static void
_add_normal_binding(GtkBindingSet * binding_set,gint key,gint alt_key,GdkModifierType mods,GtkMovementStep step,gint count)2749 _add_normal_binding(GtkBindingSet *binding_set,
2750 gint key, gint alt_key,
2751 GdkModifierType mods, GtkMovementStep step, gint count)
2752 {
2753 _add_binding_ext(binding_set,
2754 key, alt_key, mods,
2755 step, count, FALSE);
2756
2757 _add_binding_ext(binding_set,
2758 key, alt_key, GDK_SHIFT_MASK | mods,
2759 step, count, TRUE);
2760 }
2761
2762 static void
_gtk_sheet_class_init_tab_bindings(GtkSheetClass * klass,GtkDirectionType dir)2763 _gtk_sheet_class_init_tab_bindings(GtkSheetClass *klass, GtkDirectionType dir)
2764 {
2765 GtkBindingSet *b = gtk_binding_set_by_class(klass);
2766 GtkDirectionType prim_forw, prim_back, sec_forw, sec_back;
2767
2768 switch(dir)
2769 {
2770 case GTK_DIR_TAB_FORWARD:
2771 case GTK_DIR_RIGHT:
2772 prim_forw = GTK_DIR_TAB_FORWARD;
2773 prim_back = GTK_DIR_TAB_BACKWARD;
2774 sec_forw = GTK_DIR_DOWN;
2775 sec_back = GTK_DIR_UP;
2776 break;
2777 case GTK_DIR_TAB_BACKWARD:
2778 case GTK_DIR_LEFT:
2779 prim_forw = GTK_DIR_TAB_BACKWARD;
2780 prim_back = GTK_DIR_TAB_FORWARD;
2781 sec_forw = GTK_DIR_UP;
2782 sec_back = GTK_DIR_DOWN;
2783 break;
2784 case GTK_DIR_UP:
2785 prim_forw = GTK_DIR_UP;
2786 prim_back = GTK_DIR_DOWN;
2787 sec_forw = GTK_DIR_TAB_BACKWARD;
2788 sec_back = GTK_DIR_TAB_FORWARD;
2789 break;
2790 case GTK_DIR_DOWN:
2791 prim_forw = GTK_DIR_DOWN;
2792 prim_back = GTK_DIR_UP;
2793 sec_forw = GTK_DIR_TAB_FORWARD;
2794 sec_back = GTK_DIR_TAB_BACKWARD;
2795 break;
2796 }
2797
2798 /* Tab / Backtab (Normal) */
2799 _add_binding_ext(b, GDK_KEY_Tab, 0,
2800 0,
2801 GTK_MOVEMENT_LOGICAL_POSITIONS, prim_forw,
2802 FALSE);
2803
2804 _add_binding_ext(b, GDK_KEY_ISO_Left_Tab, 0,
2805 GDK_SHIFT_MASK,
2806 GTK_MOVEMENT_LOGICAL_POSITIONS, prim_back,
2807 FALSE);
2808
2809 /* Tab / Backtab (Mod-Ctrl) */
2810 _add_binding_ext(b, GDK_KEY_Tab, 0,
2811 GTK_SHEET_MOD_MASK | GDK_CONTROL_MASK,
2812 GTK_MOVEMENT_LOGICAL_POSITIONS, sec_forw,
2813 FALSE);
2814
2815 _add_binding_ext(b, GDK_KEY_ISO_Left_Tab, 0,
2816 GTK_SHEET_MOD_MASK | GDK_CONTROL_MASK | GDK_SHIFT_MASK,
2817 GTK_MOVEMENT_LOGICAL_POSITIONS, sec_back,
2818 FALSE);
2819
2820 #if 1
2821 /* Return / Enter (Normal) */
2822 _add_binding_ext(b, GDK_KEY_Return, GDK_KEY_KP_Enter,
2823 0,
2824 GTK_MOVEMENT_LOGICAL_POSITIONS, prim_forw,
2825 FALSE);
2826
2827 _add_binding_ext(b, GDK_KEY_Return, GDK_KEY_KP_Enter,
2828 GDK_SHIFT_MASK,
2829 GTK_MOVEMENT_LOGICAL_POSITIONS, prim_back,
2830 FALSE);
2831
2832 /* Return / Enter (Mod-Ctrl) */
2833 _add_binding_ext(b, GDK_KEY_Return, GDK_KEY_KP_Enter,
2834 GTK_SHEET_MOD_MASK | GDK_CONTROL_MASK,
2835 GTK_MOVEMENT_LOGICAL_POSITIONS, sec_forw,
2836 FALSE);
2837
2838 _add_binding_ext(b, GDK_KEY_Return, GDK_KEY_KP_Enter,
2839 GTK_SHEET_MOD_MASK | GDK_CONTROL_MASK | GDK_SHIFT_MASK,
2840 GTK_MOVEMENT_LOGICAL_POSITIONS, sec_back,
2841 FALSE);
2842 #endif
2843 }
2844
2845 static void
_gtk_sheet_class_init_bindings(GtkSheetClass * klass)2846 _gtk_sheet_class_init_bindings(GtkSheetClass *klass)
2847 {
2848 GtkBindingSet *b = gtk_binding_set_by_class(klass);
2849
2850 /* Up/Down (Normal) */
2851 _add_normal_binding(b, GDK_KEY_Up, GDK_KEY_KP_Up,
2852 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
2853
2854 _add_normal_binding(b, GDK_KEY_Down, GDK_KEY_KP_Down,
2855 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
2856
2857 /* Up / Down (Mod) */
2858 _add_mod_binding(b, GDK_KEY_Up, GDK_KEY_KP_Up,
2859 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
2860
2861 _add_mod_binding(b, GDK_KEY_Down, GDK_KEY_KP_Down,
2862 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
2863
2864 /* Up / Down (Mod-Ctrl) */
2865 _add_mod_binding(b, GDK_KEY_Up, GDK_KEY_KP_Up,
2866 GDK_CONTROL_MASK, GTK_MOVEMENT_PAGES, -1);
2867
2868 _add_mod_binding(b, GDK_KEY_Down, GDK_KEY_KP_Down,
2869 GDK_CONTROL_MASK, GTK_MOVEMENT_PAGES, 1);
2870
2871 /* PgUp / PgDn (Normal) */
2872 _add_normal_binding(b, GDK_KEY_Page_Up, GDK_KEY_KP_Page_Up,
2873 0, GTK_MOVEMENT_PAGES, -1);
2874
2875 _add_normal_binding(b, GDK_KEY_Page_Down, GDK_KEY_KP_Page_Down,
2876 0, GTK_MOVEMENT_PAGES, 1);
2877
2878 /* PgUp / PgDn (Mod) */
2879 _add_mod_binding(b, GDK_KEY_Page_Up, GDK_KEY_KP_Page_Up,
2880 0, GTK_MOVEMENT_PAGES, -1);
2881
2882 _add_mod_binding(b, GDK_KEY_Page_Down, GDK_KEY_KP_Page_Down,
2883 0, GTK_MOVEMENT_PAGES, 1);
2884
2885 /* Left / Right (Mod) */
2886 _add_mod_binding(b, GDK_KEY_Left, GDK_KEY_KP_Left,
2887 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
2888
2889 _add_mod_binding(b, GDK_KEY_Right, GDK_KEY_KP_Right,
2890 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
2891
2892 /* Left / Right (Mod-Ctrl) */
2893 _add_mod_binding(b, GDK_KEY_Left, GDK_KEY_KP_Left,
2894 GDK_CONTROL_MASK, GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
2895
2896 _add_mod_binding(b, GDK_KEY_Right, GDK_KEY_KP_Right,
2897 GDK_CONTROL_MASK, GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
2898
2899 /* Home / End (Mod) */
2900 _add_mod_binding(b, GDK_KEY_Home, GDK_KEY_KP_Home,
2901 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
2902
2903 _add_mod_binding(b, GDK_KEY_End, GDK_KEY_KP_End,
2904 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
2905
2906 /* Home / End (Mod-Ctrl) */
2907 _add_mod_binding(b, GDK_KEY_Home, GDK_KEY_KP_Home,
2908 GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1);
2909
2910 _add_mod_binding(b, GDK_KEY_End, GDK_KEY_KP_End,
2911 GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1);
2912
2913 /* Tab, Return, Enter */
2914 _gtk_sheet_class_init_tab_bindings(klass, GTK_DIR_TAB_FORWARD);
2915 }
2916
2917
2918
2919 /**
2920 * _gtk_sheet_binding_filter:
2921 * @sheet: The #GtkSheet
2922 * @key: The keypress event
2923 *
2924 * keypress binding filter
2925 *
2926 * Some keypress events (with no MODS) are very convenient for the user to navigate the sheet may be processed by a sheet_entry itself. For example the Up/Down keys are very handy to navigate from row to row are used by GtkTextView to move Up/Down within a multi line text.
2927 *
2928 * In order to get most out of both worlds, we generally allow useful bindings for all sheet_entry types and filter out useage collisions depending on the type of the sheet_entry.
2929 *
2930 * Returns: TRUE to activate the binding on the sheet, FALSE to
2931 * let the sheet_entry process the keypress
2932 */
_gtk_sheet_binding_filter(GtkSheet * sheet,GdkEventKey * key)2933 static gboolean _gtk_sheet_binding_filter(GtkSheet *sheet,
2934 GdkEventKey *key)
2935 {
2936 if (key->state & GTK_SHEET_MOD_MASK)
2937 return (TRUE);
2938
2939 if ( GTK_IS_DATA_TEXT_VIEW(sheet->sheet_entry)
2940 || GTK_IS_TEXT_VIEW(sheet->sheet_entry) )
2941 {
2942 switch(key->keyval)
2943 {
2944 case GDK_KEY_Return:
2945 case GDK_KEY_Up:
2946 case GDK_KEY_Down:
2947 case GDK_KEY_Page_Up:
2948 case GDK_KEY_Page_Down:
2949 return (FALSE);
2950
2951 default:
2952 break;
2953 }
2954 }
2955 return (TRUE);
2956 }
2957
2958 /*
2959 * gtk_sheet_class_init - GtkSheet class initialisation
2960 *
2961 * @param klass
2962 */
2963
2964 static void
gtk_sheet_class_init(GtkSheetClass * klass)2965 gtk_sheet_class_init(GtkSheetClass *klass)
2966 {
2967 GtkObjectClass *object_class;
2968 GtkWidgetClass *widget_class;
2969 GtkContainerClass *container_class;
2970 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
2971
2972 object_class = (GtkObjectClass *)klass;
2973 widget_class = (GtkWidgetClass *)klass;
2974 container_class = (GtkContainerClass *)klass;
2975
2976 sheet_parent_class = g_type_class_peek_parent(klass);
2977
2978 _gtk_sheet_class_init_signals(object_class, widget_class);
2979 _gtk_sheet_class_init_bindings(klass);
2980
2981 container_class->add = NULL;
2982 container_class->remove = gtk_sheet_remove_handler;
2983 container_class->forall = gtk_sheet_forall_handler;
2984
2985 object_class->destroy = gtk_sheet_destroy_handler;
2986 gobject_class->finalize = gtk_sheet_finalize_handler;
2987
2988 gtk_sheet_class_init_properties(gobject_class);
2989
2990 widget_class->realize = gtk_sheet_realize_handler;
2991 widget_class->unrealize = gtk_sheet_unrealize_handler;
2992 widget_class->map = gtk_sheet_map_handler;
2993 widget_class->unmap = gtk_sheet_unmap_handler;
2994 widget_class->style_set = gtk_sheet_style_set_handler;
2995 widget_class->button_press_event = gtk_sheet_button_press_handler;
2996 widget_class->button_release_event = gtk_sheet_button_release_handler;
2997 widget_class->motion_notify_event = gtk_sheet_motion_handler;
2998 widget_class->key_press_event = gtk_sheet_key_press_handler;
2999 widget_class->expose_event = gtk_sheet_expose_handler;
3000 widget_class->size_request = gtk_sheet_size_request_handler;
3001 widget_class->size_allocate = gtk_sheet_size_allocate_handler;
3002 widget_class->focus = gtk_sheet_focus;
3003 widget_class->focus_in_event = NULL;
3004 widget_class->focus_out_event = NULL;
3005
3006 klass->select_row = NULL;
3007 klass->select_column = NULL;
3008 klass->select_range = NULL;
3009 klass->clip_range = NULL;
3010 klass->resize_range = NULL;
3011 klass->move_range = NULL;
3012 klass->traverse = NULL;
3013 klass->deactivate = NULL;
3014 klass->activate = NULL;
3015 klass->set_cell = NULL;
3016 klass->clear_cell = NULL;
3017 klass->changed = NULL;
3018 klass->new_column_width = NULL;
3019 klass->new_row_height = NULL;
3020 klass->focus_in_event = NULL;
3021 klass->focus_out_event = NULL;
3022
3023 #if GTK_SHEET_DEBUG_SIGNALS > 0
3024 klass->select_row = gtk_sheet_debug_select_row;
3025 klass->select_column = gtk_sheet_debug_select_column;
3026 klass->select_range = gtk_sheet_debug_select_range;
3027 klass->clip_range = gtk_sheet_debug_clip_range;
3028 klass->resize_range = gtk_sheet_debug_resize_range;
3029 klass->move_range = gtk_sheet_debug_move_range;
3030 klass->traverse = gtk_sheet_debug_traverse;
3031 klass->deactivate = gtk_sheet_debug_deactivate;
3032 klass->activate = gtk_sheet_debug_activate;
3033 klass->set_cell = gtk_sheet_debug_set_cell;
3034 klass->clear_cell = gtk_sheet_debug_clear_cell;
3035 /*klass->changed = gtk_sheet_debug_changed;*/
3036 klass->new_column_width = gtk_sheet_debug_new_column_width;
3037 klass->new_row_height = gtk_sheet_debug_new_row_height;
3038 klass->focus_in_event = gtk_sheet_debug_focus_in_event;
3039 klass->focus_out_event = gtk_sheet_debug_focus_out_event;
3040 #endif
3041
3042 klass->set_scroll_adjustments = gtk_sheet_set_scroll_adjustments;
3043 klass->move_cursor = _gtk_sheet_move_cursor;
3044 }
3045
3046 static void
gtk_sheet_init(GtkSheet * sheet)3047 gtk_sheet_init(GtkSheet *sheet)
3048 {
3049 sheet->flags = 0;
3050 sheet->selection_mode = GTK_SELECTION_BROWSE;
3051 sheet->autoresize_columns = FALSE;
3052 sheet->autoresize_rows = FALSE;
3053 sheet->autoscroll = TRUE;
3054 sheet->clip_text = FALSE;
3055 sheet->justify_entry = TRUE;
3056 sheet->locked = FALSE;
3057
3058 sheet->freeze_count = 0;
3059
3060 #if GTK_SHEET_DEBUG_COLORS > 0
3061 gdk_color_parse(GTK_SHEET_DEBUG_COLOR, &debug_color);
3062 gdk_colormap_alloc_color(gdk_colormap_get_system(), &debug_color, FALSE, TRUE);
3063 #endif
3064
3065 gdk_color_parse(GTK_SHEET_DEFAULT_BG_COLOR, &sheet->bg_color);
3066 gdk_colormap_alloc_color(gdk_colormap_get_system(), &sheet->bg_color, FALSE, TRUE);
3067
3068 gdk_color_parse(GTK_SHEET_DEFAULT_GRID_COLOR, &sheet->grid_color);
3069 gdk_colormap_alloc_color(gdk_colormap_get_system(), &sheet->grid_color, FALSE, TRUE);
3070 sheet->show_grid = TRUE;
3071
3072 gdk_color_parse(GTK_SHEET_DEFAULT_TM_COLOR, &sheet->tm_color);
3073 gdk_colormap_alloc_color(gdk_colormap_get_system(), &sheet->tm_color, FALSE, TRUE);
3074
3075 sheet->children = NULL;
3076
3077 sheet->internal_allocation.x = 0;
3078 sheet->internal_allocation.y = 0;
3079 sheet->internal_allocation.width = 0;
3080 sheet->internal_allocation.height = 0;
3081
3082 sheet->title = NULL;
3083
3084 sheet->row = NULL;
3085 sheet->column = NULL;
3086
3087 sheet->rows_resizable = TRUE;
3088 sheet->columns_resizable = TRUE;
3089
3090 sheet->maxrow = -1;
3091 sheet->maxcol = -1;
3092
3093 sheet->view.row0 = -1;
3094 sheet->view.col0 = -1;
3095 sheet->view.rowi = -1;
3096 sheet->view.coli = -1;
3097
3098 sheet->data = NULL;
3099
3100 sheet->maxallocrow = -1;
3101 sheet->maxalloccol = -1;
3102
3103 sheet->active_cell.row = -1;
3104 sheet->active_cell.col = -1;
3105
3106 sheet->sheet_entry = NULL;
3107 sheet->entry_type = G_TYPE_NONE;
3108 sheet->installed_entry_type = G_TYPE_NONE;
3109
3110 sheet->selection_cell.row = -1;
3111 sheet->selection_cell.col = -1;
3112
3113 sheet->timer = 0;
3114 sheet->clip_timer = 0;
3115 sheet->interval = 0;
3116
3117 sheet->button = NULL;
3118 sheet->state = GTK_SHEET_NORMAL;
3119
3120 sheet->range.row0 = -1;
3121 sheet->range.rowi = -1;
3122 sheet->range.col0 = -1;
3123 sheet->range.coli = -1;
3124
3125 sheet->sheet_window = NULL;
3126 sheet->sheet_window_width = 0;
3127 sheet->sheet_window_height = 0;
3128
3129 sheet->pixmap = NULL;
3130
3131 sheet->hoffset = 0;
3132 sheet->voffset = 0;
3133
3134 sheet->old_hadjustment = 0;
3135 sheet->old_vadjustment = 0;
3136
3137 sheet->hadjustment = NULL;
3138 sheet->vadjustment = NULL;
3139
3140 sheet->shadow_type = GTK_SHADOW_NONE;
3141 sheet->vjust = GTK_SHEET_VERTICAL_JUSTIFICATION_TOP;
3142
3143 sheet->column_title_area.x = 0;
3144 sheet->column_title_area.y = 0;
3145 sheet->column_title_area.width = 0;
3146 sheet->column_title_area.height = _gtk_sheet_row_default_height(GTK_WIDGET(sheet));
3147 sheet->column_title_window = NULL;
3148 sheet->column_titles_visible = TRUE;
3149
3150 sheet->row_title_window = NULL;
3151 sheet->row_title_area.x = 0;
3152 sheet->row_title_area.y = 0;
3153 sheet->row_title_area.width = GTK_SHEET_COLUMN_DEFAULT_WIDTH;
3154 sheet->row_title_area.height = 0;
3155 sheet->row_titles_visible = TRUE;
3156
3157 sheet->xor_gc = NULL;
3158 sheet->fg_gc = NULL;
3159 sheet->bg_gc = NULL;
3160
3161 sheet->cursor_drag = gdk_cursor_new(GDK_PLUS);
3162 sheet->x_drag = 0;
3163 sheet->y_drag = 0;
3164
3165 /* uninitialized
3166 sheet->drag_cell;
3167 sheet->drag_range;
3168 sheet->clip_range;
3169 */
3170
3171 gtk_widget_set_has_window(GTK_WIDGET(sheet), TRUE);
3172 gtk_widget_set_can_focus(GTK_WIDGET(sheet), TRUE);
3173
3174 /* for glade to be able to construct the object, we need to complete initialisation here */
3175 gtk_sheet_construct(sheet, 0, 0, "GtkSheet");
3176
3177 g_signal_connect(G_OBJECT(sheet),
3178 "query-tooltip",
3179 (void *)gtk_sheet_query_tooltip_handler,
3180 NULL);
3181
3182 /* make sure the tooltip signal fires even if the sheet itself has no tooltip */
3183 gtk_widget_set_has_tooltip(GTK_WIDGET(sheet), TRUE);
3184 }
3185
3186
3187 static void
_gtk_sheet_row_init(GtkSheetRow * row)3188 _gtk_sheet_row_init(GtkSheetRow *row)
3189 {
3190 row->name = NULL;
3191 row->height = GTK_SHEET_ROW_DEFAULT_HEIGHT;
3192 row->requisition = GTK_SHEET_ROW_DEFAULT_HEIGHT;
3193 row->top_ypixel = 0;
3194 row->max_extent_height = 0;
3195
3196 row->button.state = GTK_STATE_NORMAL;
3197 row->button.label = NULL;
3198 row->button.label_visible = TRUE;
3199 row->button.child = NULL;
3200 row->button.justification = GTK_JUSTIFY_CENTER;
3201
3202 row->tooltip_markup = NULL;
3203 row->tooltip_text = NULL;
3204
3205 GTK_SHEET_ROW_SET_VISIBLE(row, TRUE);
3206 GTK_SHEET_ROW_SET_SENSITIVE(row, TRUE);
3207 GTK_SHEET_ROW_SET_READONLY(row, FALSE);
3208 GTK_SHEET_ROW_SET_CAN_FOCUS(row, TRUE);
3209 }
3210
3211 static void
gtk_sheet_row_finalize(GtkSheetRow * row)3212 gtk_sheet_row_finalize(GtkSheetRow *row)
3213 {
3214 if (row->name)
3215 {
3216 g_free(row->name);
3217 row->name = NULL;
3218 }
3219
3220 if (row->button.label)
3221 {
3222 g_free(row->button.label);
3223 row->button.label = NULL;
3224 }
3225
3226 if (row->tooltip_markup)
3227 {
3228 g_free(row->tooltip_markup);
3229 row->tooltip_markup = NULL;
3230 }
3231
3232 if (row->tooltip_text)
3233 {
3234 g_free(row->tooltip_text);
3235 row->tooltip_text = NULL;
3236 }
3237 }
3238
3239 /**
3240 * gtk_sheet_new:
3241 * @rows: initial number of rows
3242 * @columns: initial number of columns
3243 * @title: sheet title
3244 *
3245 * Creates a new sheet widget with the given number of rows and columns.
3246 *
3247 * Returns: the new sheet #GtkSheet
3248 */
3249 GtkWidget *
gtk_sheet_new(guint rows,guint columns,const gchar * title)3250 gtk_sheet_new(guint rows, guint columns, const gchar *title)
3251 {
3252 GtkWidget *widget;
3253
3254 /* sanity check */
3255 g_return_val_if_fail(columns >= MINCOLS, NULL);
3256 g_return_val_if_fail(rows >= MINROWS, NULL);
3257
3258 widget = gtk_widget_new(gtk_sheet_get_type(), NULL);
3259
3260 gtk_sheet_construct(GTK_SHEET(widget), rows, columns, title);
3261
3262 return (widget);
3263 }
3264
3265 /**
3266 * gtk_sheet_construct:
3267 * @sheet: a #GtkSheet
3268 * @rows: number of rows
3269 * @columns: number of columns
3270 * @title: sheet title
3271 *
3272 * Initializes an existent #GtkSheet with the given number of rows and columns.
3273 */
3274 void
gtk_sheet_construct(GtkSheet * sheet,guint rows,guint columns,const gchar * title)3275 gtk_sheet_construct(GtkSheet *sheet, guint rows, guint columns, const gchar *title)
3276 {
3277 sheet->data = (GtkSheetCell ***)g_malloc(sizeof(GtkSheetCell **));
3278
3279 sheet->data[0] = (GtkSheetCell **)g_malloc(sizeof(GtkSheetCell *)+sizeof(gdouble));
3280 sheet->data[0][0] = NULL;
3281
3282 /* set number of rows and columns */
3283 GrowSheet(sheet, MINROWS, MINCOLS);
3284
3285 /* Init heading row/column, add normal rows/columns */
3286 AddRows(sheet, sheet->maxrow + 1, rows);
3287 AddColumns(sheet, sheet->maxcol + 1, columns);
3288
3289 /* create sheet entry */
3290 create_sheet_entry(sheet, G_TYPE_NONE);
3291
3292 /* create global selection button */
3293 create_global_button(sheet);
3294
3295 if (title)
3296 {
3297 if (sheet->title)
3298 g_free(sheet->title);
3299 sheet->title = g_strdup(title);
3300 }
3301 }
3302
3303 /**
3304 * gtk_sheet_new_browser:
3305 * @rows: initial number of rows
3306 * @columns: initial number of columns
3307 * @title: sheet title
3308 *
3309 * Creates a new browser sheet. Its cells cannot be edited(read-only).
3310 *
3311 * Returns: the new read-only #GtkSheet
3312 */
3313 GtkWidget *
gtk_sheet_new_browser(guint rows,guint columns,const gchar * title)3314 gtk_sheet_new_browser(guint rows, guint columns, const gchar *title)
3315 {
3316 GtkWidget *widget;
3317
3318 widget = gtk_widget_new(gtk_sheet_get_type(), NULL);
3319
3320 gtk_sheet_construct_browser(GTK_SHEET(widget), rows, columns, title);
3321
3322 return (widget);
3323 }
3324
3325 /**
3326 * gtk_sheet_construct_browser:
3327 * @sheet: a #GtkSheet
3328 * @rows: number of rows
3329 * @columns: number of columns
3330 * @title: sheet title
3331 *
3332 * Initializes an existent read-only #GtkSheet with the given number of rows and columns.
3333 */
3334 void
gtk_sheet_construct_browser(GtkSheet * sheet,guint rows,guint columns,const gchar * title)3335 gtk_sheet_construct_browser(GtkSheet *sheet, guint rows, guint columns,
3336 const gchar *title)
3337 {
3338 gtk_sheet_construct(sheet, rows, columns, title);
3339
3340 gtk_sheet_set_locked(sheet, TRUE);
3341 }
3342
3343 /**
3344 * gtk_sheet_new_with_custom_entry:
3345 * @rows: initial number of rows
3346 * @columns: initial number of columns
3347 * @title: sheet title
3348 * @entry_type: a #GType
3349 *
3350 * Creates a new sheet widget with the given number of rows and columns and a custome entry type.
3351 *
3352 * Returns: the new sheet #GtkSheet
3353 */
3354 GtkWidget *
gtk_sheet_new_with_custom_entry(guint rows,guint columns,const gchar * title,GType entry_type)3355 gtk_sheet_new_with_custom_entry(guint rows, guint columns, const gchar *title,
3356 GType entry_type)
3357 {
3358 GtkWidget *widget;
3359
3360 widget = gtk_widget_new(gtk_sheet_get_type(), NULL);
3361
3362 gtk_sheet_construct_with_custom_entry(GTK_SHEET(widget),
3363 rows, columns, title,
3364 entry_type ? entry_type : G_TYPE_NONE);
3365
3366 return (widget);
3367 }
3368
3369 /**
3370 * gtk_sheet_construct_with_custom_entry:
3371 * @sheet: a #GtkSheet
3372 * @rows: number of rows
3373 * @columns: number of columns
3374 * @title: sheet title
3375 * @entry_type: a #GType
3376 *
3377 * Initializes an existent read-only #GtkSheet
3378 * with the given number of rows and columns and a custom entry.
3379 */
3380 void
gtk_sheet_construct_with_custom_entry(GtkSheet * sheet,guint rows,guint columns,const gchar * title,GType entry_type)3381 gtk_sheet_construct_with_custom_entry(GtkSheet *sheet,
3382 guint rows, guint columns,
3383 const gchar *title,
3384 GType entry_type)
3385 {
3386 gtk_sheet_construct(sheet, rows, columns, title);
3387
3388 create_sheet_entry(sheet, entry_type ? entry_type : G_TYPE_NONE);
3389 }
3390
3391 /**
3392 * gtk_sheet_change_entry:
3393 * @sheet: a #GtkSheet
3394 * @entry_type: a #GType
3395 *
3396 * Changes the current entry of the cell in #GtkSheet. The old
3397 * sheet entry widget gets dropped and a new entry widget is
3398 * created. Beware: You will have to reconnect all your signal
3399 * handlers after changing an entry.
3400 */
3401 void
gtk_sheet_change_entry(GtkSheet * sheet,const GType entry_type)3402 gtk_sheet_change_entry(GtkSheet *sheet, const GType entry_type)
3403 {
3404 gint state;
3405
3406 g_return_if_fail(sheet != NULL);
3407 g_return_if_fail(GTK_IS_SHEET(sheet));
3408
3409 state = sheet->state;
3410
3411 if (state == GTK_SHEET_NORMAL)
3412 _gtk_sheet_hide_active_cell(sheet);
3413
3414 create_sheet_entry(sheet, entry_type ? entry_type : G_TYPE_NONE);
3415
3416 sheet->entry_type = entry_type; /* save wanted type, no matter wether it failed */
3417
3418 if (state == GTK_SHEET_NORMAL)
3419 {
3420 gtk_sheet_show_active_cell(sheet);
3421
3422 /* if the application changes the entry during emission of the TRAVERSE signal
3423 an initialisation of the new entry can fire the "changed" signal and
3424 the text will be written into the old active cell (which is WRONG)
3425
3426 gtk_sheet_entry_signal_connect_changed(sheet,
3427 G_CALLBACK(gtk_sheet_entry_changed_handler));
3428 */
3429 }
3430 }
3431
3432 /**
3433 * gtk_sheet_show_grid:
3434 * @sheet: a #GtkSheet
3435 * @show: TRUE(grid visible) or FALSE(grid invisible)
3436 *
3437 * Sets the visibility of grid in #GtkSheet.
3438 */
3439 void
gtk_sheet_show_grid(GtkSheet * sheet,gboolean show)3440 gtk_sheet_show_grid(GtkSheet *sheet, gboolean show)
3441 {
3442 g_return_if_fail(sheet != NULL);
3443 g_return_if_fail(GTK_IS_SHEET(sheet));
3444
3445 if (show == sheet->show_grid)
3446 return;
3447
3448 sheet->show_grid = show;
3449
3450 if (!GTK_SHEET_IS_FROZEN(sheet))
3451 _gtk_sheet_range_draw(sheet, NULL, TRUE);
3452 }
3453
3454 /**
3455 * gtk_sheet_grid_visible:
3456 * @sheet: a #GtkSheet
3457 *
3458 * Gets the visibility of grid in #GtkSheet.
3459 *
3460 * Returns: TRUE(grid visible) or FALSE(grid invisible)
3461 */
3462 gboolean
gtk_sheet_grid_visible(GtkSheet * sheet)3463 gtk_sheet_grid_visible(GtkSheet *sheet)
3464 {
3465 g_return_val_if_fail(sheet != NULL, 0);
3466 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
3467
3468 return (sheet->show_grid);
3469 }
3470
3471 /**
3472 * gtk_sheet_set_background:
3473 * @sheet: a #GtkSheet
3474 * @color: a #GdkColor structure
3475 *
3476 * Sets the background color of the #GtkSheet.
3477 * If pass NULL, the sheet will be reset to the default color.
3478 */
3479 void
gtk_sheet_set_background(GtkSheet * sheet,GdkColor * color)3480 gtk_sheet_set_background(GtkSheet *sheet, GdkColor *color)
3481 {
3482 g_return_if_fail(sheet != NULL);
3483 g_return_if_fail(GTK_IS_SHEET(sheet));
3484
3485 if (!color)
3486 {
3487 gdk_color_parse(GTK_SHEET_DEFAULT_BG_COLOR, &sheet->bg_color);
3488 }
3489 else
3490 {
3491 sheet->bg_color = *color;
3492 }
3493 gdk_colormap_alloc_color(gdk_colormap_get_system(), &sheet->bg_color, FALSE, TRUE);
3494
3495 if (!GTK_SHEET_IS_FROZEN(sheet))
3496 _gtk_sheet_range_draw(sheet, NULL, TRUE);
3497 }
3498
3499 /**
3500 * gtk_sheet_set_grid:
3501 * @sheet: a #GtkSheet
3502 * @color: a #GdkColor structure
3503 *
3504 * Set the grid color.
3505 * If pass NULL, the grid will be reset to the default color.
3506 */
3507 void
gtk_sheet_set_grid(GtkSheet * sheet,GdkColor * color)3508 gtk_sheet_set_grid(GtkSheet *sheet, GdkColor *color)
3509 {
3510 g_return_if_fail(sheet != NULL);
3511 g_return_if_fail(GTK_IS_SHEET(sheet));
3512
3513 if (!color)
3514 {
3515 gdk_color_parse(GTK_SHEET_DEFAULT_GRID_COLOR, &sheet->grid_color);
3516 }
3517 else
3518 {
3519 sheet->grid_color = *color;
3520 }
3521 gdk_colormap_alloc_color(gdk_colormap_get_system(), &sheet->grid_color, FALSE, TRUE);
3522
3523 if (!GTK_SHEET_IS_FROZEN(sheet))
3524 _gtk_sheet_range_draw(sheet, NULL, TRUE);
3525 }
3526
3527 /**
3528 * gtk_sheet_get_rows_count:
3529 * @sheet: a #GtkSheet
3530 *
3531 * Get the number of the rows of the #GtkSheet.
3532 *
3533 * Returns: number of rows.
3534 */
3535 guint
gtk_sheet_get_rows_count(GtkSheet * sheet)3536 gtk_sheet_get_rows_count(GtkSheet *sheet)
3537 {
3538 g_return_val_if_fail(sheet != NULL, 0);
3539 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
3540
3541 return (sheet->maxrow + 1);
3542 }
3543
3544 /**
3545 * gtk_sheet_get_columns_count:
3546 * @sheet: a #GtkSheet
3547 *
3548 * Get the number of the columns of the #GtkSheet.
3549 *
3550 * Returns: number of columns.
3551 */
3552 guint
gtk_sheet_get_columns_count(GtkSheet * sheet)3553 gtk_sheet_get_columns_count(GtkSheet *sheet)
3554 {
3555 g_return_val_if_fail(sheet != NULL, 0);
3556 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
3557
3558 return (sheet->maxcol + 1);
3559 }
3560
3561
3562 /**
3563 * gtk_sheet_get_state:
3564 * @sheet: a #GtkSheet
3565 *
3566 * Get the selection state of the sheet (#GtkSheetState).
3567 *
3568 * Returns:
3569 * GTK_SHEET_NORMAL,GTK_SHEET_ROW_SELECTED,GTK_SHEET_COLUMN_SELECTED,GTK_SHEET_RANGE_SELECTED
3570 */
3571 GtkSheetState
gtk_sheet_get_state(GtkSheet * sheet)3572 gtk_sheet_get_state(GtkSheet *sheet)
3573 {
3574 g_return_val_if_fail(sheet != NULL, 0);
3575 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
3576
3577 return (sheet->state);
3578 }
3579
3580 /**
3581 * gtk_sheet_get_selection:
3582 * @sheet: a #GtkSheet
3583 * @state: where to store the #GtkSheetState, may be NULL
3584 * @range: where to store the #GtkSheetRange
3585 *
3586 * Inquire current cell selection state and range.
3587 *
3588 * Returns: TRUE: there is a selection, FALSE: no selection or error
3589 */
3590 gboolean
gtk_sheet_get_selection(GtkSheet * sheet,GtkSheetState * state,GtkSheetRange * range)3591 gtk_sheet_get_selection(GtkSheet *sheet, GtkSheetState *state, GtkSheetRange *range)
3592 {
3593 g_return_val_if_fail(sheet != NULL, FALSE);
3594 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
3595 g_return_val_if_fail(range != NULL, FALSE);
3596
3597 if (state)
3598 *state = sheet->state;
3599
3600 *range = sheet->range;
3601
3602 return (TRUE);
3603 }
3604
3605 /**
3606 * gtk_sheet_set_selection_mode:
3607 * @sheet: a #GtkSheet
3608 * @mode: GTK_SELECTION_SINGLE or GTK_SELECTION_BROWSE
3609 *
3610 * Sets the selection mode of the cells in a #GtkSheet.
3611 */
3612 void
gtk_sheet_set_selection_mode(GtkSheet * sheet,GtkSelectionMode mode)3613 gtk_sheet_set_selection_mode(GtkSheet *sheet, GtkSelectionMode mode)
3614 {
3615 g_return_if_fail(sheet != NULL);
3616 g_return_if_fail(GTK_IS_SHEET(sheet));
3617
3618 gtk_sheet_real_unselect_range(sheet, NULL);
3619
3620 sheet->selection_mode = mode;
3621 }
3622
3623 /**
3624 * gtk_sheet_set_autoresize:
3625 * @sheet: a #GtkSheet
3626 * @autoresize: TRUE or FALSE
3627 *
3628 * Controls wether cells will be autoresized upon deactivation,
3629 * as you type text or set a cell_text value. If you want the
3630 * cells to be autoresized when you pack widgets look at
3631 * gtk_sheet_attach_*(). This function sets both:
3632 * autoresize_columns and autoresize_cells.
3633 */
3634 void
gtk_sheet_set_autoresize(GtkSheet * sheet,gboolean autoresize)3635 gtk_sheet_set_autoresize(GtkSheet *sheet, gboolean autoresize)
3636 {
3637 g_return_if_fail(sheet != NULL);
3638 g_return_if_fail(GTK_IS_SHEET(sheet));
3639
3640 sheet->autoresize_columns = autoresize;
3641 sheet->autoresize_rows = autoresize;
3642 }
3643
3644 /**
3645 * gtk_sheet_set_autoresize_columns:
3646 * @sheet: a #GtkSheet
3647 * @autoresize: TRUE or FALSE
3648 *
3649 * Controls wether columns will be autoresized upon
3650 * deactivation, as you type text or set a cell_text value. If
3651 * you want the cells to be autoresized when you pack widgets
3652 * look at gtk_sheet_attach_*().
3653 */
3654 void
gtk_sheet_set_autoresize_columns(GtkSheet * sheet,gboolean autoresize)3655 gtk_sheet_set_autoresize_columns(GtkSheet *sheet, gboolean autoresize)
3656 {
3657 g_return_if_fail(sheet != NULL);
3658 g_return_if_fail(GTK_IS_SHEET(sheet));
3659
3660 sheet->autoresize_columns = autoresize;
3661 }
3662
3663 /**
3664 * gtk_sheet_set_autoresize_rows:
3665 * @sheet: a #GtkSheet
3666 * @autoresize: TRUE or FALSE
3667 *
3668 * Controls wether rows will be autoresized upon deactivation,
3669 * as you type text or set a cell_text value. If you want the
3670 * cells to be autoresized when you pack widgets look at
3671 * gtk_sheet_attach_*().
3672 */
3673 void
gtk_sheet_set_autoresize_rows(GtkSheet * sheet,gboolean autoresize)3674 gtk_sheet_set_autoresize_rows(GtkSheet *sheet, gboolean autoresize)
3675 {
3676 g_return_if_fail(sheet != NULL);
3677 g_return_if_fail(GTK_IS_SHEET(sheet));
3678
3679 sheet->autoresize_rows = autoresize;
3680 }
3681
3682
3683 /**
3684 * gtk_sheet_autoresize:
3685 * @sheet: a #GtkSheet
3686 *
3687 * Gets the autoresize mode of #GtkSheet.
3688 *
3689 * Returns: TRUE if autoresize_columns or autoresize_rows was
3690 * set, or FALSE if none
3691 */
3692 gboolean
gtk_sheet_autoresize(GtkSheet * sheet)3693 gtk_sheet_autoresize(GtkSheet *sheet)
3694 {
3695 g_return_val_if_fail(sheet != NULL, FALSE);
3696 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
3697
3698 return (sheet->autoresize_columns || sheet->autoresize_rows);
3699 }
3700
3701 /**
3702 * gtk_sheet_autoresize_columns:
3703 * @sheet: a #GtkSheet
3704 *
3705 * Gets the autoresize mode for #GtkSheet columns.
3706 *
3707 * Returns: TRUE or FALSE
3708 */
3709 gboolean
gtk_sheet_autoresize_columns(GtkSheet * sheet)3710 gtk_sheet_autoresize_columns(GtkSheet *sheet)
3711 {
3712 g_return_val_if_fail(sheet != NULL, FALSE);
3713 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
3714
3715 return (sheet->autoresize_columns);
3716 }
3717
3718 /**
3719 * gtk_sheet_autoresize_rows:
3720 * @sheet: a #GtkSheet
3721 *
3722 * Gets the autoresize mode for #GtkSheet rows.
3723 *
3724 * Returns: TRUE or FALSE
3725 */
3726 gboolean
gtk_sheet_autoresize_rows(GtkSheet * sheet)3727 gtk_sheet_autoresize_rows(GtkSheet *sheet)
3728 {
3729 g_return_val_if_fail(sheet != NULL, FALSE);
3730 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
3731
3732 return (sheet->autoresize_rows);
3733 }
3734
3735 /**
3736 * _gtk_sheet_recalc_extent_width:
3737 * @sheet: the #GtkSheet
3738 * @col: column to be recalculated
3739 *
3740 * recalc maximum column extent width
3741 */
_gtk_sheet_recalc_extent_width(GtkSheet * sheet,gint col)3742 static void _gtk_sheet_recalc_extent_width(GtkSheet *sheet, gint col)
3743 {
3744 gint new_width = 0;
3745 gint row;
3746
3747 #if GTK_SHEET_DEBUG_SIZE > 0
3748 g_debug("_gtk_sheet_recalc_extent_width[%d]: called", col);
3749 #endif
3750
3751 if (col < 0 || col > sheet->maxalloccol || col > sheet->maxcol)
3752 return;
3753
3754 for (row = 0; row <= sheet->maxallocrow; row++)
3755 {
3756 GtkSheetRow *rowptr = ROWPTR(sheet, row);
3757
3758 if (GTK_SHEET_ROW_IS_VISIBLE(rowptr))
3759 {
3760 GtkSheetCell *cell = sheet->data[row][col];
3761
3762 if (cell && cell->text && cell->text[0])
3763 {
3764 GtkSheetCellAttr attributes;
3765 gtk_sheet_get_attributes(sheet, row, col, &attributes);
3766
3767 if (attributes.is_visible)
3768 {
3769 if (cell->extent.width > new_width)
3770 {
3771 new_width = cell->extent.width;
3772 }
3773 }
3774 }
3775 }
3776 }
3777
3778 #if GTK_SHEET_DEBUG_SIZE > 0
3779 g_debug("_gtk_sheet_recalc_extent_width[%d]: new_width %d",
3780 col, new_width);
3781 #endif
3782
3783 COLPTR(sheet, col)->max_extent_width = new_width;
3784 }
3785
3786 /**
3787 * _gtk_sheet_recalc_extent_height:
3788 * @sheet: the #GtkSheet
3789 * @row: row to be recalculated
3790 *
3791 * recalc maximum row extent height
3792 */
_gtk_sheet_recalc_extent_height(GtkSheet * sheet,gint row)3793 static void _gtk_sheet_recalc_extent_height(GtkSheet *sheet, gint row)
3794 {
3795 gint new_height = 0;
3796 gint col;
3797
3798 #if GTK_SHEET_DEBUG_SIZE > 0
3799 g_debug("_gtk_sheet_recalc_extent_height[%d]: called", row);
3800 #endif
3801
3802 if (row < 0 || row > sheet->maxallocrow || row > sheet->maxrow)
3803 return;
3804
3805 for (col = 0; col <= sheet->maxalloccol; col++)
3806 {
3807 GtkSheetColumn *colptr = COLPTR(sheet, col);
3808
3809 if (GTK_SHEET_COLUMN_IS_VISIBLE(colptr))
3810 {
3811 GtkSheetCell *cell = sheet->data[row][col];
3812
3813 if (cell && cell->text && cell->text[0])
3814 {
3815 GtkSheetCellAttr attributes;
3816 gtk_sheet_get_attributes(sheet, row, col, &attributes);
3817
3818 if (attributes.is_visible)
3819 {
3820 if (cell->extent.height > new_height)
3821 {
3822 new_height = cell->extent.height;
3823 }
3824 }
3825 }
3826 }
3827 }
3828
3829 #if GTK_SHEET_DEBUG_SIZE > 0
3830 g_debug("_gtk_sheet_recalc_extent_height[%d]: new_height %d",
3831 col, new_height);
3832 #endif
3833
3834 ROWPTR(sheet, row)->max_extent_height = new_height;
3835 }
3836
3837 /**
3838 * _gtk_sheet_update_extent:
3839 * @sheet: the #GtkSheet
3840 * @cell: the #GtkSheetCell
3841 * @row: the row
3842 * @col: the column
3843 *
3844 * update cell extent and propagate to max row/column extent
3845 */
_gtk_sheet_update_extent(GtkSheet * sheet,GtkSheetCell * cell,gint row,gint col)3846 static void _gtk_sheet_update_extent(GtkSheet *sheet,
3847 GtkSheetCell *cell, gint row, gint col)
3848 {
3849 guint text_width = 0, text_height = 0;
3850 guint new_extent_width = 0, new_extent_height = 0;
3851 GdkRectangle old_extent;
3852 GtkSheetColumn *colptr = COLPTR(sheet, col);
3853 GtkSheetRow *rowptr = ROWPTR(sheet, row);
3854
3855 g_return_if_fail(sheet != NULL);
3856 g_return_if_fail(GTK_IS_SHEET(sheet));
3857 g_return_if_fail(cell != NULL);
3858
3859 #if GTK_SHEET_DEBUG_SIZE > 0
3860 g_debug("_gtk_sheet_update_extent[%d,%d]: called cell (xw %d,xh %d) colxw %d rowxh %d",
3861 row, col,
3862 cell->extent.width, cell->extent.height,
3863 colptr->max_extent_width, rowptr->max_extent_height);
3864 #endif
3865
3866 old_extent = cell->extent; /* to check wether it was increased */
3867
3868 if (!cell->text || !cell->text[0])
3869 {
3870 cell->extent.width = 0;
3871 cell->extent.height = 0;
3872
3873 if (old_extent.height != 0)
3874 _gtk_sheet_recalc_extent_height(sheet, row);
3875 if (old_extent.width != 0)
3876 _gtk_sheet_recalc_extent_width(sheet, col);
3877 return;
3878 }
3879
3880 GtkSheetCellAttr attributes;
3881 gtk_sheet_get_attributes(sheet, row, col, &attributes);
3882
3883 _get_string_extent(sheet, colptr,
3884 attributes.font_desc, cell->text, &text_width, &text_height);
3885
3886 /* add borders */
3887 new_extent_width = CELL_EXTENT_WIDTH(text_width, attributes.border.width);
3888 new_extent_height = CELL_EXTENT_HEIGHT(text_height, 0);
3889
3890 cell->extent.width = new_extent_width;
3891 cell->extent.height = new_extent_height;
3892
3893 if (!GTK_SHEET_COLUMN_IS_VISIBLE(colptr))
3894 return;
3895 if (!GTK_SHEET_ROW_IS_VISIBLE(rowptr))
3896 return;
3897
3898 if (new_extent_width >= old_extent.width) /* wider */
3899 {
3900 if (new_extent_width > colptr->max_extent_width)
3901 {
3902 colptr->max_extent_width = new_extent_width;
3903 }
3904 }
3905 else if (new_extent_width < old_extent.width) /* narrower */
3906 {
3907 _gtk_sheet_recalc_extent_width(sheet, col);
3908 }
3909
3910 if (new_extent_height >= old_extent.height) /* higher */
3911 {
3912 if (new_extent_height > rowptr->max_extent_height)
3913 {
3914 rowptr->max_extent_height = new_extent_height;
3915 }
3916 }
3917 else if (new_extent_height < old_extent.height) /* lower */
3918 {
3919 _gtk_sheet_recalc_extent_height(sheet, row);
3920 }
3921
3922 #if GTK_SHEET_DEBUG_SIZE > 0
3923 g_debug("_gtk_sheet_update_extent[%d,%d]: done cell (xw %d,xh %d) colxw %d rowxh %d",
3924 row, col,
3925 cell->extent.width, cell->extent.height,
3926 colptr->max_extent_width, rowptr->max_extent_height);
3927 #endif
3928 }
3929
3930 static void
_gtk_sheet_autoresize_column_internal(GtkSheet * sheet,gint col)3931 _gtk_sheet_autoresize_column_internal(GtkSheet *sheet, gint col)
3932 {
3933 gint new_width;
3934 GtkSheetColumn *colptr;
3935
3936 g_return_if_fail(sheet != NULL);
3937 g_return_if_fail(GTK_IS_SHEET(sheet));
3938
3939 if (col < 0 || col > sheet->maxalloccol || col > sheet->maxcol)
3940 return;
3941
3942 colptr = COLPTR(sheet, col);
3943
3944 if (!GTK_SHEET_COLUMN_IS_VISIBLE(colptr))
3945 return;
3946
3947 new_width = COLUMN_EXTENT_TO_WIDTH(colptr->max_extent_width);
3948
3949 #if GTK_SHEET_DEBUG_SIZE > 0
3950 g_debug("_gtk_sheet_autoresize_column_internal[%d]: called col w %d new w %d",
3951 col, colptr->width, new_width);
3952 #endif
3953
3954 if (new_width != colptr->width)
3955 {
3956 #if GTK_SHEET_DEBUG_SIZE > 0
3957 g_debug("_gtk_sheet_autoresize_column_internal[%d]: set width %d",
3958 col, new_width);
3959 #endif
3960 gtk_sheet_set_column_width(sheet, col, new_width);
3961 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_REDRAW_PENDING);
3962 }
3963 }
3964
3965 static void
_gtk_sheet_autoresize_row_internal(GtkSheet * sheet,gint row)3966 _gtk_sheet_autoresize_row_internal(GtkSheet *sheet, gint row)
3967 {
3968 gint new_height;
3969 GtkSheetRow *rowptr;
3970
3971 g_return_if_fail(sheet != NULL);
3972 g_return_if_fail(GTK_IS_SHEET(sheet));
3973
3974 if (row < 0 || row > sheet->maxallocrow || row > sheet->maxrow)
3975 return;
3976
3977 rowptr = ROWPTR(sheet, row);
3978
3979 if (!GTK_SHEET_ROW_IS_VISIBLE(rowptr))
3980 return;
3981
3982 new_height = ROW_EXTENT_TO_HEIGHT(rowptr->max_extent_height);
3983
3984 #if 0 && GTK_SHEET_DEBUG_SIZE > 0
3985 g_debug("_gtk_sheet_autoresize_row_internal[%d]: win_h %d ext_h %d row_max_h %d",
3986 row, sheet->sheet_window_height, rowptr->max_extent_height,
3987 ROW_MAX_HEIGHT(sheet));
3988 g_debug("_gtk_sheet_autoresize_row_internal[%d]: called row h %d new h %d",
3989 row, rowptr->height, new_height);
3990 #endif
3991
3992 if (new_height != rowptr->height)
3993 {
3994 #if GTK_SHEET_DEBUG_SIZE > 0
3995 g_debug("_gtk_sheet_autoresize_row_internal[%d]: set height %d",
3996 row, new_height);
3997 #endif
3998 gtk_sheet_set_row_height(sheet, row, new_height);
3999 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_REDRAW_PENDING);
4000 }
4001 }
4002
gtk_sheet_autoresize_all(GtkSheet * sheet)4003 static void gtk_sheet_autoresize_all(GtkSheet *sheet)
4004 {
4005 g_return_if_fail(sheet != NULL);
4006 g_return_if_fail(GTK_IS_SHEET(sheet));
4007
4008 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
4009 {
4010 #if GTK_SHEET_DEBUG_SIZE > 0
4011 g_debug("gtk_sheet_autoresize_all: not realized");
4012 #endif
4013 return;
4014 }
4015
4016 #if GTK_SHEET_DEBUG_SIZE > 0
4017 g_debug("gtk_sheet_autoresize_all: running");
4018 #endif
4019
4020 gboolean need_freeze = (
4021 !GTK_SHEET_IS_FROZEN(sheet)
4022 && (gtk_sheet_autoresize_columns(sheet)
4023 || gtk_sheet_autoresize_rows(sheet))
4024 );
4025
4026 if (need_freeze) gtk_sheet_freeze(sheet);
4027
4028 if (gtk_sheet_autoresize_columns(sheet))
4029 {
4030 gint col;
4031
4032 #if GTK_SHEET_DEBUG_SIZE > 0
4033 g_debug("gtk_sheet_autoresize_all: columns");
4034 #endif
4035
4036 for (col = 0; col <= sheet->maxalloccol; col++)
4037 {
4038 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)))
4039 {
4040 _gtk_sheet_autoresize_column_internal(sheet, col);
4041 }
4042 }
4043 }
4044
4045 if (gtk_sheet_autoresize_rows(sheet))
4046 {
4047 gint row;
4048
4049 #if GTK_SHEET_DEBUG_SIZE > 0
4050 g_debug("gtk_sheet_autoresize_all: rows");
4051 #endif
4052
4053 for (row = 0; row <= sheet->maxallocrow; row++)
4054 {
4055 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)))
4056 {
4057 _gtk_sheet_autoresize_row_internal(sheet, row);
4058 }
4059 }
4060 }
4061
4062 if (need_freeze) gtk_sheet_thaw(sheet);
4063 }
4064
4065 /**
4066 * gtk_sheet_set_autoscroll:
4067 * @sheet: a #GtkSheet
4068 * @autoscroll: TRUE or FALSE
4069 *
4070 * The sheet will be automatically scrolled when you move beyond
4071 * the last row/col in #GtkSheet.
4072 */
4073 void
gtk_sheet_set_autoscroll(GtkSheet * sheet,gboolean autoscroll)4074 gtk_sheet_set_autoscroll(GtkSheet *sheet, gboolean autoscroll)
4075 {
4076 g_return_if_fail(sheet != NULL);
4077 g_return_if_fail(GTK_IS_SHEET(sheet));
4078
4079 sheet->autoscroll = autoscroll;
4080 }
4081
4082 /**
4083 * gtk_sheet_autoscroll:
4084 * @sheet: a #GtkSheet
4085 *
4086 * Get the autoscroll mode of #GtkSheet.
4087 *
4088 * Returns: TRUE or FALSE
4089 */
4090 gboolean
gtk_sheet_autoscroll(GtkSheet * sheet)4091 gtk_sheet_autoscroll(GtkSheet *sheet)
4092 {
4093 g_return_val_if_fail(sheet != NULL, FALSE);
4094 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
4095
4096 return (sheet->autoscroll);
4097 }
4098
4099 /**
4100 * gtk_sheet_set_clip_text:
4101 * @sheet: a #GtkSheet
4102 * @clip_text: TRUE or FALSE
4103 *
4104 * Clip text in cell. When clip text mode is turned off, cell
4105 * text is written over neighbour columns, as long as their
4106 * contents are empty.
4107 */
4108 void
gtk_sheet_set_clip_text(GtkSheet * sheet,gboolean clip_text)4109 gtk_sheet_set_clip_text(GtkSheet *sheet, gboolean clip_text)
4110 {
4111 g_return_if_fail(sheet != NULL);
4112 g_return_if_fail(GTK_IS_SHEET(sheet));
4113
4114 sheet->clip_text = clip_text;
4115 }
4116
4117 /**
4118 * gtk_sheet_clip_text:
4119 * @sheet: a #GtkSheet
4120 *
4121 * Get clip text mode in #GtkSheet. When clip text mode is
4122 * turned off, cell text is written over neighbour columns, as
4123 * long as their contents are empty.
4124 *
4125 * Returns: TRUE or FALSE
4126 */
4127 gboolean
gtk_sheet_clip_text(GtkSheet * sheet)4128 gtk_sheet_clip_text(GtkSheet *sheet)
4129 {
4130 g_return_val_if_fail(sheet != NULL, FALSE);
4131 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
4132
4133 return (sheet->clip_text);
4134 }
4135
4136 /**
4137 * gtk_sheet_set_justify_entry:
4138 * @sheet: a #GtkSheet
4139 * @justify: TRUE or FALSE
4140 *
4141 * Justify cell entry editor in #GtkSheet.
4142 */
4143 void
gtk_sheet_set_justify_entry(GtkSheet * sheet,gboolean justify)4144 gtk_sheet_set_justify_entry(GtkSheet *sheet, gboolean justify)
4145 {
4146 g_return_if_fail(sheet != NULL);
4147 g_return_if_fail(GTK_IS_SHEET(sheet));
4148
4149 sheet->justify_entry = justify;
4150 }
4151
4152 /**
4153 * gtk_sheet_justify_entry:
4154 * @sheet: a #GtkSheet
4155 *
4156 * Get the cell entry editor justification setting from
4157 * #GtkSheet.
4158 *
4159 * Returns: TRUE or FALSE
4160 */
4161 gboolean
gtk_sheet_justify_entry(GtkSheet * sheet)4162 gtk_sheet_justify_entry(GtkSheet *sheet)
4163 {
4164 g_return_val_if_fail(sheet != NULL, FALSE);
4165 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
4166
4167 return (sheet->justify_entry);
4168 }
4169
4170 /**
4171 * gtk_sheet_set_vjustification:
4172 * @sheet: a #GtkSheet
4173 * @vjust: a #GtkSheetVerticalJustification
4174 *
4175 * Set the default vertical cell text justification for
4176 * #GtkSheet.
4177 */
4178 void
gtk_sheet_set_vjustification(GtkSheet * sheet,GtkSheetVerticalJustification vjust)4179 gtk_sheet_set_vjustification(GtkSheet *sheet, GtkSheetVerticalJustification vjust)
4180 {
4181 g_return_if_fail(sheet != NULL);
4182 g_return_if_fail(GTK_IS_SHEET(sheet));
4183
4184 sheet->vjust = vjust;
4185 }
4186
4187 /**
4188 * gtk_sheet_get_vjustification:
4189 * @sheet: a #GtkSheet
4190 *
4191 * Get the default vertical cell text justification from
4192 * #GtkSheet.
4193 *
4194 * Returns: the default #GtkSheetVerticalJustification
4195 */
4196 GtkSheetVerticalJustification
gtk_sheet_get_vjustification(GtkSheet * sheet)4197 gtk_sheet_get_vjustification(GtkSheet *sheet)
4198 {
4199 g_return_val_if_fail(sheet != NULL, FALSE);
4200 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
4201
4202 return (sheet->vjust);
4203 }
4204
4205 /**
4206 * gtk_sheet_set_traverse_type:
4207 * @sheet: a #GtkSheet
4208 * @ttype: a #GtkSheetTraverseType
4209 *
4210 * Set the default traversal type for cursor movement in a
4211 * #GtkSheet.
4212 */
4213 void
gtk_sheet_set_traverse_type(GtkSheet * sheet,GtkSheetTraverseType ttype)4214 gtk_sheet_set_traverse_type(GtkSheet *sheet, GtkSheetTraverseType ttype)
4215 {
4216 g_return_if_fail(sheet != NULL);
4217 g_return_if_fail(GTK_IS_SHEET(sheet));
4218
4219 sheet->traverse_type = ttype;
4220 }
4221
4222 /**
4223 * gtk_sheet_get_traverse_type:
4224 * @sheet: a #GtkSheet
4225 *
4226 * Get the default cell traversal type from #GtkSheet.
4227 *
4228 * Returns: the default #GtkSheetTraverseType
4229 */
4230 GtkSheetTraverseType
gtk_sheet_get_traverse_type(GtkSheet * sheet)4231 gtk_sheet_get_traverse_type(GtkSheet *sheet)
4232 {
4233 g_return_val_if_fail(sheet != NULL, FALSE);
4234 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
4235
4236 return (sheet->traverse_type);
4237 }
4238
4239
4240
4241 /**
4242 * gtk_sheet_set_locked:
4243 * @sheet: a #GtkSheet
4244 * @locked: TRUE or FALSE
4245 *
4246 * Lock the #GtkSheet, which means it is no longer editable,
4247 * cell contents cannot be modified by the user.
4248 */
4249 void
gtk_sheet_set_locked(GtkSheet * sheet,gboolean locked)4250 gtk_sheet_set_locked(GtkSheet *sheet, gboolean locked)
4251 {
4252 g_return_if_fail(sheet != NULL);
4253 g_return_if_fail(GTK_IS_SHEET(sheet));
4254
4255 sheet->locked = locked;
4256 }
4257
4258 /**
4259 * gtk_sheet_locked:
4260 * @sheet: a #GtkSheet
4261 *
4262 * Get the lock status of #GtkSheet, locked means the sheet is
4263 * not editable, cell contents cannot be modified by the user.
4264 *
4265 * Returns: TRUE or FALSE
4266 */
4267 gboolean
gtk_sheet_locked(GtkSheet * sheet)4268 gtk_sheet_locked(GtkSheet *sheet)
4269 {
4270 g_return_val_if_fail(sheet != NULL, FALSE);
4271 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
4272
4273 return (sheet->locked);
4274 }
4275
4276 /* This routine has problems with gtk+-1.2 related with the
4277 * label/button drawing - I think it's a bug in gtk+-1.2 */
4278
4279 /**
4280 * gtk_sheet_set_title:
4281 * @sheet: a #GtkSheet
4282 * @title: #GtkSheet title
4283 *
4284 * Set #GtkSheet title. The widget will keep a copy of the
4285 * string.
4286 */
4287 void
gtk_sheet_set_title(GtkSheet * sheet,const gchar * title)4288 gtk_sheet_set_title(GtkSheet *sheet, const gchar *title)
4289 {
4290 GtkWidget *label;
4291
4292 g_return_if_fail(sheet != NULL);
4293 g_return_if_fail(GTK_IS_SHEET(sheet));
4294
4295 if (sheet->title)
4296 {
4297 g_free(sheet->title);
4298 sheet->title = NULL;
4299 }
4300 if (title)
4301 {
4302 sheet->title = g_strdup(title);
4303 }
4304
4305 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)) || !title)
4306 return;
4307
4308 if (gtk_bin_get_child(GTK_BIN(sheet->button)))
4309 label = gtk_bin_get_child(GTK_BIN(sheet->button));
4310 /*
4311 gtk_label_set_text(GTK_LABEL(label), title);
4312 */
4313 size_allocate_global_button(sheet);
4314
4315 /* remove and destroy the old widget */
4316 /*
4317 old_widget = gtk_bin_get_child(GTK_BIN (sheet->button));
4318 if (old_widget)
4319 {
4320 gtk_container_remove (GTK_CONTAINER (sheet->button), old_widget);
4321 }
4322
4323 label = gtk_label_new (title);
4324 gtk_misc_set_alignment(GTK_MISC(label), 0.5 , 0.5 );
4325
4326 gtk_container_add (GTK_CONTAINER (sheet->button), label);
4327 gtk_widget_show (label);
4328
4329 size_allocate_global_button(sheet);
4330
4331 g_signal_emit(GTK_OBJECT(sheet),sheet_signals[CHANGED], 0, -1, -1);
4332
4333 if(old_widget)
4334 gtk_widget_destroy (old_widget);
4335 */
4336 }
4337
4338 /**
4339 * gtk_sheet_set_description:
4340 * @sheet: a #GtkSheet
4341 * @description: #GtkSheet description
4342 *
4343 * Set #GtkSheet description for application use.
4344 */
4345 void
gtk_sheet_set_description(GtkSheet * sheet,const gchar * description)4346 gtk_sheet_set_description(GtkSheet *sheet, const gchar *description)
4347 {
4348 g_return_if_fail(sheet != NULL);
4349 g_return_if_fail(GTK_IS_SHEET(sheet));
4350
4351 if (sheet->description)
4352 g_free(sheet->description);
4353 sheet->description = g_strdup(description);
4354 }
4355
4356 /**
4357 * gtk_sheet_get_description:
4358 * @sheet: a #GtkSheet
4359 * @description: #GtkSheet description
4360 *
4361 * Get sheet description.
4362 *
4363 * Returns: sheet description or NULL, do not modify or free it
4364 */
4365 const gchar *
gtk_sheet_get_description(GtkSheet * sheet,const gchar * description)4366 gtk_sheet_get_description(GtkSheet *sheet, const gchar *description)
4367 {
4368 g_return_val_if_fail(sheet != NULL, NULL);
4369 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
4370
4371 return (sheet->description);
4372 }
4373
4374
4375 /**
4376 * gtk_sheet_is_frozen:
4377 * @sheet: the #GtkSheet
4378 *
4379 * Get freeze status.
4380 *
4381 * Returns: TRUE or FALSE wether the sheet is frozen
4382 */
gtk_sheet_is_frozen(GtkSheet * sheet)4383 gboolean gtk_sheet_is_frozen(GtkSheet *sheet)
4384 {
4385 return (GTK_SHEET_IS_FROZEN(sheet));
4386 }
4387
4388
4389 /**
4390 * gtk_sheet_freeze:
4391 * @sheet: a #GtkSheet
4392 *
4393 * Freeze all visual updates of the #GtkSheet.
4394 * The updates will occure in a more efficient way than if you made them on a unfrozen #GtkSheet .
4395 */
4396 void
gtk_sheet_freeze(GtkSheet * sheet)4397 gtk_sheet_freeze(GtkSheet *sheet)
4398 {
4399 g_return_if_fail(sheet != NULL);
4400 g_return_if_fail(GTK_IS_SHEET(sheet));
4401
4402 sheet->freeze_count++;
4403 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IS_FROZEN);
4404
4405 #if GTK_SHEET_DEBUG_FREEZE > 0
4406 g_debug("gtk_sheet_freeze: freeze_count == %d", sheet->freeze_count);
4407 #endif
4408
4409 }
4410
4411
4412 /**
4413 * _gtk_sheet_redraw_internal:
4414 * @sheet: to be redrawn
4415 * @reset_hadjustment: wether to reset horizontal adjustment
4416 * @reset_vadjustment: wether to reset vertical adjustment
4417 *
4418 * do a complete sheet redraw. used after rows/cols have been
4419 * appended/deleted or after combined operations while the
4420 * sheet was frozen
4421 */
_gtk_sheet_redraw_internal(GtkSheet * sheet,gboolean reset_hadjustment,gboolean reset_vadjustment)4422 void _gtk_sheet_redraw_internal(GtkSheet *sheet,
4423 gboolean reset_hadjustment,
4424 gboolean reset_vadjustment)
4425 {
4426 gboolean done = FALSE; /* handle sheets with no scrollbars */
4427
4428 if (reset_hadjustment)
4429 sheet->old_hadjustment = -1.; /* causes redraw */
4430 if (reset_vadjustment)
4431 sheet->old_vadjustment = -1.; /* causes redraw */
4432
4433 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
4434 return;
4435 if (GTK_SHEET_IS_FROZEN(sheet))
4436 return;
4437
4438 #if GTK_SHEET_DEBUG_DRAW > 0
4439 g_debug("_gtk_sheet_redraw_internal: called");
4440 #endif
4441
4442 _gtk_sheet_recalc_view_range(sheet);
4443
4444 if (sheet->row_titles_visible || sheet->column_titles_visible)
4445 {
4446 size_allocate_global_button(sheet);
4447 }
4448
4449 if (sheet->row_titles_visible)
4450 {
4451 size_allocate_row_title_buttons(sheet);
4452 }
4453
4454 if (sheet->column_titles_visible)
4455 {
4456 _gtk_sheet_column_buttons_size_allocate(sheet);
4457 }
4458
4459 /* send value_changed or redraw directly */
4460
4461 if (sheet->vadjustment)
4462 {
4463 g_signal_emit_by_name(GTK_OBJECT(sheet->vadjustment), "value_changed");
4464 done = TRUE;
4465 }
4466
4467 if (sheet->hadjustment)
4468 {
4469 g_signal_emit_by_name(GTK_OBJECT(sheet->hadjustment), "value_changed");
4470 done = TRUE;
4471 }
4472
4473 if (!done)
4474 {
4475 _gtk_sheet_range_draw(sheet, NULL, TRUE);
4476 }
4477 }
4478
4479
4480 /**
4481 * gtk_sheet_thaw:
4482 * @sheet: a #GtkSheet
4483 *
4484 * Thaw the sheet after you have made a number of changes on a frozen sheet.
4485 * The updates will occure in a more efficient way than if you made them on a unfrozen sheet .
4486 */
4487 void
gtk_sheet_thaw(GtkSheet * sheet)4488 gtk_sheet_thaw(GtkSheet *sheet)
4489 {
4490 g_return_if_fail(sheet != NULL);
4491 g_return_if_fail(GTK_IS_SHEET(sheet));
4492
4493 if (sheet->freeze_count == 0)
4494 return;
4495
4496 sheet->freeze_count--;
4497 if (sheet->freeze_count > 0)
4498 {
4499 #if GTK_SHEET_DEBUG_FREEZE > 0
4500 g_warning("gtk_sheet_thaw: ignored (freeze_count > 0)");
4501 #endif
4502 return;
4503 }
4504 #if GTK_SHEET_DEBUG_FREEZE > 0
4505 g_debug("gtk_sheet_thaw: freeze_count == %d", sheet->freeze_count);
4506 #endif
4507
4508 _gtk_sheet_scrollbar_adjust(sheet);
4509
4510 if (gtk_widget_get_realized(GTK_WIDGET(sheet)))
4511 {
4512 if (sheet->row_titles_visible)
4513 {
4514 size_allocate_row_title_buttons(sheet);
4515 gdk_window_show(sheet->row_title_window);
4516 }
4517
4518 if (sheet->column_titles_visible)
4519 {
4520 _gtk_sheet_column_buttons_size_allocate(sheet);
4521 gdk_window_show(sheet->column_title_window);
4522 }
4523 }
4524
4525 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN);
4526
4527 if (gtk_sheet_autoresize(sheet))
4528 {
4529 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_AUTORESIZE_PENDING);
4530 }
4531
4532 _gtk_sheet_redraw_internal(sheet, TRUE, TRUE);
4533
4534 if (sheet->state == GTK_STATE_NORMAL)
4535 {
4536 if (sheet->sheet_entry && gtk_widget_get_mapped(sheet->sheet_entry))
4537 {
4538 gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col);
4539 /*
4540 gtk_sheet_entry_signal_connect_changed(sheet, gtk_sheet_entry_changed_handler);
4541 gtk_sheet_show_active_cell(sheet);
4542 */
4543 }
4544 }
4545 }
4546
4547 /**
4548 * gtk_sheet_set_row_titles_width:
4549 * @sheet: a #GtkSheet
4550 * @width: row titles width.
4551 *
4552 * Resize row titles area.
4553 */
4554 void
gtk_sheet_set_row_titles_width(GtkSheet * sheet,guint width)4555 gtk_sheet_set_row_titles_width(GtkSheet *sheet, guint width)
4556 {
4557 if (width < GTK_SHEET_COLUMN_MIN_WIDTH)
4558 return;
4559
4560 sheet->row_title_area.width = width;
4561
4562 _gtk_sheet_recalc_top_ypixels(sheet);
4563 _gtk_sheet_recalc_left_xpixels(sheet);
4564 _gtk_sheet_recalc_view_range(sheet);
4565
4566 _gtk_sheet_scrollbar_adjust(sheet);
4567 _gtk_sheet_redraw_internal(sheet, TRUE, FALSE);
4568 }
4569
4570
4571 /**
4572 * gtk_sheet_show_row_titles:
4573 * @sheet: a #GtkSheet
4574 *
4575 * Show row titles .
4576 */
4577 void
gtk_sheet_show_row_titles(GtkSheet * sheet)4578 gtk_sheet_show_row_titles(GtkSheet *sheet)
4579 {
4580 gint row;
4581
4582 if (sheet->row_titles_visible)
4583 return;
4584
4585 sheet->row_titles_visible = TRUE;
4586 _gtk_sheet_recalc_top_ypixels(sheet);
4587 _gtk_sheet_recalc_left_xpixels(sheet);
4588
4589 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
4590 return;
4591 if (GTK_SHEET_IS_FROZEN(sheet))
4592 return;
4593
4594 gdk_window_show(sheet->row_title_window);
4595
4596 gdk_window_move_resize(sheet->row_title_window,
4597 sheet->row_title_area.x,
4598 sheet->row_title_area.y,
4599 sheet->row_title_area.width,
4600 sheet->row_title_area.height);
4601
4602 for (row = MIN_VIEW_ROW(sheet); row <= MAX_VIEW_ROW(sheet) && row <= sheet->maxrow; row++)
4603 {
4604 GtkSheetChild *child;
4605 if (row < 0 || row > sheet->maxrow)
4606 continue;
4607
4608 child = sheet->row[row].button.child;
4609 if (child)
4610 _gtk_sheet_child_show(child);
4611 }
4612 _gtk_sheet_scrollbar_adjust(sheet);
4613 _gtk_sheet_redraw_internal(sheet, TRUE, FALSE);
4614 }
4615
4616
4617 /**
4618 * gtk_sheet_hide_row_titles:
4619 * @sheet: a #GtkSheet
4620 *
4621 * Hide row titles .
4622 */
4623 void
gtk_sheet_hide_row_titles(GtkSheet * sheet)4624 gtk_sheet_hide_row_titles(GtkSheet *sheet)
4625 {
4626 gint row;
4627
4628 if (!sheet->row_titles_visible)
4629 return;
4630
4631 sheet->row_titles_visible = FALSE;
4632 _gtk_sheet_recalc_top_ypixels(sheet);
4633 _gtk_sheet_recalc_left_xpixels(sheet);
4634
4635 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
4636 return;
4637 if (GTK_SHEET_IS_FROZEN(sheet))
4638 return;
4639
4640 if (sheet->row_title_window)
4641 gdk_window_hide(sheet->row_title_window);
4642
4643 if (gtk_widget_get_visible(sheet->button))
4644 gtk_widget_hide(sheet->button);
4645
4646 for (row = MIN_VIEW_ROW(sheet); row <= MAX_VIEW_ROW(sheet) && row <= sheet->maxrow; row++)
4647 {
4648 GtkSheetChild *child;
4649 if (row < 0 || row > sheet->maxrow)
4650 continue;
4651
4652 child = sheet->row[row].button.child;
4653 if (child)
4654 _gtk_sheet_child_hide(child);
4655 }
4656 _gtk_sheet_scrollbar_adjust(sheet);
4657 _gtk_sheet_redraw_internal(sheet, TRUE, FALSE);
4658 }
4659
4660 /**
4661 * gtk_sheet_row_titles_visible:
4662 * @sheet: a #GtkSheet
4663 *
4664 * Get the visibility of row column titles .
4665 *
4666 * Returns: TRUE or FALSE
4667 */
4668 gboolean
gtk_sheet_row_titles_visible(GtkSheet * sheet)4669 gtk_sheet_row_titles_visible(GtkSheet *sheet)
4670 {
4671 g_return_val_if_fail(sheet != NULL, FALSE);
4672 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
4673
4674 return (sheet->row_titles_visible);
4675 }
4676
4677
4678 /**
4679 * gtk_sheet_set_row_title:
4680 * @sheet: a #GtkSheet
4681 * @row: row number
4682 * @title: row title
4683 *
4684 * Set row title.
4685 */
4686 void
gtk_sheet_set_row_title(GtkSheet * sheet,gint row,const gchar * title)4687 gtk_sheet_set_row_title(GtkSheet *sheet,
4688 gint row,
4689 const gchar *title)
4690 {
4691 g_return_if_fail(sheet != NULL);
4692 g_return_if_fail(GTK_IS_SHEET(sheet));
4693
4694 if (sheet->row[row].name)
4695 g_free(sheet->row[row].name);
4696
4697 sheet->row[row].name = g_strdup(title);
4698 }
4699
4700 /**
4701 * gtk_sheet_get_row_title:
4702 * @sheet: a #GtkSheet
4703 * @row: row number
4704 *
4705 * Get row title.
4706 *
4707 * Returns: a pointer to the row title or NULL.
4708 * Do not modify or free it.
4709 */
4710 const gchar *
gtk_sheet_get_row_title(GtkSheet * sheet,gint row)4711 gtk_sheet_get_row_title(GtkSheet *sheet,
4712 gint row)
4713 {
4714 g_return_val_if_fail(sheet != NULL, NULL);
4715 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
4716
4717 return (sheet->row[row].name);
4718 }
4719
4720 /**
4721 * gtk_sheet_row_button_add_label:
4722 * @sheet: a #GtkSheet
4723 * @row: row number
4724 * @label: text label
4725 *
4726 * Set button label.It is used to set a row title.
4727 */
4728 void
gtk_sheet_row_button_add_label(GtkSheet * sheet,gint row,const gchar * label)4729 gtk_sheet_row_button_add_label(GtkSheet *sheet, gint row, const gchar *label)
4730 {
4731 GtkSheetButton *button;
4732 GtkRequisition req;
4733 gboolean aux_c, aux_r;
4734
4735 g_return_if_fail(sheet != NULL);
4736 g_return_if_fail(GTK_IS_SHEET(sheet));
4737
4738 if (row < 0 || row > sheet->maxrow)
4739 return;
4740
4741 button = &sheet->row[row].button;
4742 if (button->label)
4743 g_free(button->label);
4744 button->label = g_strdup(label);
4745
4746 aux_c = gtk_sheet_autoresize_columns(sheet);
4747 aux_r = gtk_sheet_autoresize_rows(sheet);
4748 gtk_sheet_set_autoresize(sheet, FALSE);
4749 gtk_sheet_set_autoresize_rows(sheet, TRUE);
4750 _gtk_sheet_button_size_request(sheet, button, &req);
4751 gtk_sheet_set_autoresize_columns(sheet, aux_c);
4752 gtk_sheet_set_autoresize_rows(sheet, aux_r);
4753
4754 if (req.height > sheet->row[row].height)
4755 gtk_sheet_set_row_height(sheet, row, req.height);
4756
4757 if (req.width > sheet->row_title_area.width)
4758 {
4759 gtk_sheet_set_row_titles_width(sheet, req.width);
4760 }
4761
4762 if (!GTK_SHEET_IS_FROZEN(sheet))
4763 {
4764 _gtk_sheet_draw_button(sheet, row, -1);
4765 }
4766 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[CHANGED], 0, row, -1);
4767 }
4768
4769 /**
4770 * gtk_sheet_row_button_get_label:
4771 * @sheet: a #GtkSheet
4772 * @row: row number
4773 *
4774 * Get a row button label.
4775 *
4776 * Returns: In case of succes , a pointer to label
4777 * text.Otherwise NULL
4778 */
4779 const gchar *
gtk_sheet_row_button_get_label(GtkSheet * sheet,gint row)4780 gtk_sheet_row_button_get_label(GtkSheet *sheet, gint row)
4781 {
4782 g_return_val_if_fail(sheet != NULL, NULL);
4783 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
4784
4785 if (row < 0 || row > sheet->maxrow)
4786 return (NULL);
4787
4788 return (sheet->row[row].button.label);
4789 }
4790
4791 /**
4792 * gtk_sheet_row_label_set_visibility:
4793 * @sheet: a #GtkSheet
4794 * @row: row number
4795 * @visible: TRUE or FALSE
4796 *
4797 * Set row label visibility.
4798 */
4799 void
gtk_sheet_row_label_set_visibility(GtkSheet * sheet,gint row,gboolean visible)4800 gtk_sheet_row_label_set_visibility(GtkSheet *sheet, gint row, gboolean visible)
4801 {
4802 g_return_if_fail(sheet != NULL);
4803 g_return_if_fail(GTK_IS_SHEET(sheet));
4804
4805 if (row < 0 || row > sheet->maxrow)
4806 return;
4807
4808 sheet->row[row].button.label_visible = visible;
4809
4810 if (!GTK_SHEET_IS_FROZEN(sheet))
4811 {
4812 _gtk_sheet_draw_button(sheet, row, -1);
4813 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[CHANGED], 0, row, -1);
4814 }
4815 }
4816
4817 /**
4818 * gtk_sheet_rows_labels_set_visibility:
4819 * @sheet: a #GtkSheet
4820 * @visible: TRUE or FALSE
4821 *
4822 * Set all rows label visibility. The sheet itself
4823 * has no such property, this is a convenience function to set
4824 * the property for all existing rows.
4825 */
4826 void
gtk_sheet_rows_labels_set_visibility(GtkSheet * sheet,gboolean visible)4827 gtk_sheet_rows_labels_set_visibility(GtkSheet *sheet, gboolean visible)
4828 {
4829 gint i;
4830
4831 g_return_if_fail(sheet != NULL);
4832 g_return_if_fail(GTK_IS_SHEET(sheet));
4833
4834 for (i = 0; i <= sheet->maxrow; i++) gtk_sheet_row_label_set_visibility(sheet, i, visible);
4835 }
4836
4837
4838 /**
4839 * gtk_sheet_row_button_justify:
4840 * @sheet: a #GtkSheet.
4841 * @row: row number
4842 * @justification: a #GtkJustification :GTK_JUSTIFY_LEFT, RIGHT,
4843 * CENTER
4844 *
4845 * Set the justification(alignment) of the row buttons.
4846 */
4847 void
gtk_sheet_row_button_justify(GtkSheet * sheet,gint row,GtkJustification justification)4848 gtk_sheet_row_button_justify(GtkSheet *sheet, gint row,
4849 GtkJustification justification)
4850 {
4851 GtkSheetButton *button;
4852
4853 g_return_if_fail(sheet != NULL);
4854 g_return_if_fail(GTK_IS_SHEET(sheet));
4855
4856 if (row < 0 || row > sheet->maxrow)
4857 return;
4858
4859 button = &sheet->row[row].button;
4860 button->justification = justification;
4861
4862 if (!GTK_SHEET_IS_FROZEN(sheet))
4863 {
4864 _gtk_sheet_draw_button(sheet, row, -1);
4865 }
4866 }
4867
4868 /**
4869 * gtk_sheet_moveto:
4870 * @sheet: a #GtkSheet.
4871 * @row: row number
4872 * @column: column number
4873 * @row_align: row alignment
4874 * @col_align: column alignment
4875 *
4876 * Scroll the viewing area of the sheet to the given column and row;
4877 *
4878 * row_align and col_align are between 0-1 representing the location
4879 * the row should appear on the screnn, 0 being top or left,
4880 * 1 being bottom or right.
4881 *
4882 * passing row_align/col_align of -1 will suppress movement in
4883 * that direction.
4884 *
4885 * if row or column is negative then there is no change
4886 */
4887
4888 void
gtk_sheet_moveto(GtkSheet * sheet,gint row,gint col,gint row_align,gint col_align)4889 gtk_sheet_moveto(GtkSheet *sheet,
4890 gint row,
4891 gint col,
4892 gint row_align,
4893 gint col_align)
4894 {
4895 gint x, y;
4896 guint width, height;
4897
4898 g_return_if_fail(sheet != NULL);
4899 g_return_if_fail(GTK_IS_SHEET(sheet));
4900 g_return_if_fail(sheet->hadjustment != NULL);
4901 g_return_if_fail(sheet->vadjustment != NULL);
4902
4903 #if GTK_SHEET_DEBUG_MOVE > 0
4904 g_debug("gtk_sheet_moveto: row %d col %d row_align %d col_align %d",
4905 row, col, row_align, col_align);
4906 #endif
4907
4908 if (row < 0 || row > sheet->maxrow)
4909 return;
4910 if (col < 0 || col > sheet->maxcol)
4911 return;
4912
4913 height = sheet->sheet_window_height;
4914 width = sheet->sheet_window_width;
4915
4916 /* adjust vertical scrollbar */
4917
4918 if (row >= 0 && row_align >= 0)
4919 {
4920 if (row_align == 0) /* align top cell border */
4921 {
4922 y = _gtk_sheet_row_top_ypixel(sheet, row) - sheet->voffset;
4923
4924 if (sheet->column_titles_visible)
4925 y -= sheet->column_title_area.height; /* to bottom edge of title area*/
4926 }
4927 else /* align bottom cell border */
4928 {
4929 y = _gtk_sheet_row_top_ypixel(sheet, row) - sheet->voffset
4930 + sheet->row[row].height;
4931
4932 y -= height; /* to bottom edge of window */
4933 }
4934
4935 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
4936 g_debug("gtk_sheet_moveto: rowTpx %d voffs %d height %d rheight %d colTw %s y %d",
4937 _gtk_sheet_row_top_ypixel(sheet, row), sheet->voffset,
4938 height, sheet->row[row].height,
4939 sheet->column_titles_visible ? "Yes" : "No",
4940 y);
4941 #endif
4942
4943 if (y < 0)
4944 gtk_adjustment_set_value(sheet->vadjustment, 0.0);
4945 else
4946 gtk_adjustment_set_value(sheet->vadjustment, y);
4947
4948 sheet->old_vadjustment = -1.;
4949
4950 if (sheet->vadjustment)
4951 {
4952 g_signal_emit_by_name(GTK_OBJECT(sheet->vadjustment), "value_changed");
4953 }
4954 }
4955
4956 /* adjust horizontal scrollbar */
4957 if (col >= 0 && col_align >= 0) /* left or right align */
4958 {
4959 if (col_align == 0) /* align left cell border */
4960 {
4961 x = _gtk_sheet_column_left_xpixel(sheet, col) - sheet->hoffset;
4962
4963 if (sheet->row_titles_visible)
4964 x -= sheet->row_title_area.width; /* to right edge of title area*/
4965 }
4966 else /* align right cell border */
4967 {
4968 x = _gtk_sheet_column_left_xpixel(sheet, col) - sheet->hoffset
4969 + COLPTR(sheet, col)->width;
4970
4971 x -= width; /* to right edge of window */
4972 }
4973
4974 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
4975 g_debug("gtk_sheet_moveto: colLpx %d hoffs %d width %d cwidth %d rowTw %s x %d",
4976 _gtk_sheet_column_left_xpixel(sheet, col), sheet->hoffset,
4977 width, COLPTR(sheet, col)->width,
4978 sheet->row_titles_visible ? "Yes" : "No",
4979 x);
4980 #endif
4981
4982 if (x < 0)
4983 gtk_adjustment_set_value(sheet->hadjustment, 0.0);
4984 else
4985 gtk_adjustment_set_value(sheet->hadjustment, x);
4986
4987 sheet->old_hadjustment = -1.;
4988
4989 if (sheet->hadjustment)
4990 {
4991 g_signal_emit_by_name(GTK_OBJECT(sheet->hadjustment), "value_changed");
4992 }
4993 }
4994 }
4995
4996
4997 /**
4998 * gtk_sheet_row_sensitive:
4999 * @sheet: a #GtkSheet.
5000 * @row: row number
5001 *
5002 * Get row button sensitivity.
5003 *
5004 * Returns:
5005 * TRUE - is sensitive,
5006 * FALSE - insensitive or not existant
5007 */
5008 gboolean
gtk_sheet_row_sensitive(GtkSheet * sheet,gint row)5009 gtk_sheet_row_sensitive(GtkSheet *sheet, gint row)
5010 {
5011 g_return_val_if_fail(sheet != NULL, FALSE);
5012 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5013
5014 if (row < 0 || row > sheet->maxrow)
5015 return (FALSE);
5016
5017 return (GTK_SHEET_ROW_IS_SENSITIVE(ROWPTR(sheet, row)));
5018 }
5019
5020 /**
5021 * gtk_sheet_row_set_sensitivity:
5022 * @sheet: a #GtkSheet.
5023 * @row: row number
5024 * @sensitive: TRUE or FALSE
5025 *
5026 * Set row button sensitivity. If sensitivity is TRUE can be toggled, otherwise it acts as a title .
5027 */
5028 void
gtk_sheet_row_set_sensitivity(GtkSheet * sheet,gint row,gboolean sensitive)5029 gtk_sheet_row_set_sensitivity(GtkSheet *sheet, gint row, gboolean sensitive)
5030 {
5031 g_return_if_fail(sheet != NULL);
5032 g_return_if_fail(GTK_IS_SHEET(sheet));
5033
5034 if (row < 0 || row > sheet->maxrow)
5035 return;
5036
5037 GTK_SHEET_ROW_SET_SENSITIVE(ROWPTR(sheet, row), sensitive);
5038
5039 if (!sensitive)
5040 sheet->row[row].button.state = GTK_STATE_INSENSITIVE;
5041 else
5042 sheet->row[row].button.state = GTK_STATE_NORMAL;
5043
5044 if (gtk_widget_get_realized(GTK_WIDGET(sheet)) && !GTK_SHEET_IS_FROZEN(sheet))
5045 _gtk_sheet_draw_button(sheet, row, -1);
5046 }
5047
5048 /**
5049 * gtk_sheet_rows_set_sensitivity:
5050 * @sheet: a #GtkSheet.
5051 * @sensitive: TRUE or FALSE
5052 *
5053 * Set rows buttons sensitivity. If sensitivity is TRUE button
5054 * can be toggled, otherwise act as titles. The sheet itself
5055 * has no such property, it is a convenience function to set the
5056 * property for all existing rows.
5057 */
5058 void
gtk_sheet_rows_set_sensitivity(GtkSheet * sheet,gboolean sensitive)5059 gtk_sheet_rows_set_sensitivity(GtkSheet *sheet, gboolean sensitive)
5060 {
5061 gint i;
5062
5063 g_return_if_fail(sheet != NULL);
5064 g_return_if_fail(GTK_IS_SHEET(sheet));
5065
5066 for (i = 0; i <= sheet->maxrow; i++) gtk_sheet_row_set_sensitivity(sheet, i, sensitive);
5067 }
5068
5069 /**
5070 * gtk_sheet_rows_set_resizable:
5071 * @sheet: a #GtkSheet.
5072 * @resizable: TRUE or FALSE
5073 *
5074 * Set rows resizable status.
5075 */
5076 void
gtk_sheet_rows_set_resizable(GtkSheet * sheet,gboolean resizable)5077 gtk_sheet_rows_set_resizable(GtkSheet *sheet, gboolean resizable)
5078 {
5079 g_return_if_fail(sheet != NULL);
5080 g_return_if_fail(GTK_IS_SHEET(sheet));
5081
5082 sheet->rows_resizable = resizable;
5083 }
5084
5085 /**
5086 * gtk_sheet_rows_resizable:
5087 * @sheet: a #GtkSheet.
5088 *
5089 * Get rows resizable status.
5090 *
5091 * Returns: TRUE or FALSE
5092 */
5093 gboolean
gtk_sheet_rows_resizable(GtkSheet * sheet)5094 gtk_sheet_rows_resizable(GtkSheet *sheet)
5095 {
5096 g_return_val_if_fail(sheet != NULL, FALSE);
5097 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5098
5099 return (sheet->rows_resizable);
5100 }
5101
5102
5103 /**
5104 * gtk_sheet_row_visible:
5105 * @sheet: a #GtkSheet.
5106 * @row: row number
5107 *
5108 * Get row visibility.
5109 *
5110 * Returns:
5111 * TRUE - is visible
5112 * FALSE - invisible or not existant
5113 */
5114 gboolean
gtk_sheet_row_visible(GtkSheet * sheet,gint row)5115 gtk_sheet_row_visible(GtkSheet *sheet, gint row)
5116 {
5117 g_return_val_if_fail(sheet != NULL, FALSE);
5118 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5119
5120 if (row < 0 || row > sheet->maxrow)
5121 return (FALSE);
5122
5123 return (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)));
5124 }
5125
5126 /**
5127 * gtk_sheet_row_set_visibility:
5128 * @sheet: a #GtkSheet.
5129 * @row: row number
5130 * @visible: TRUE or FALSE
5131 *
5132 * Set row visibility. The default value is TRUE. If FALSE, the row is hidden.
5133 */
5134 void
gtk_sheet_row_set_visibility(GtkSheet * sheet,gint row,gboolean visible)5135 gtk_sheet_row_set_visibility(GtkSheet *sheet, gint row, gboolean visible)
5136 {
5137 GtkSheetRow *rowobj;
5138 gint act_row, act_col;
5139
5140 g_return_if_fail(sheet != NULL);
5141 g_return_if_fail(GTK_IS_SHEET(sheet));
5142
5143 if (row < 0 || row > sheet->maxrow)
5144 return;
5145
5146 rowobj = ROWPTR(sheet, row);
5147 if (GTK_SHEET_ROW_IS_VISIBLE(rowobj) == visible)
5148 return;
5149
5150 act_row = sheet->active_cell.row;
5151 act_col = sheet->active_cell.col;
5152
5153 if (act_row == row) /* hide active column -> disable active cell */
5154 {
5155 _gtk_sheet_hide_active_cell(sheet);
5156
5157 sheet->active_cell.row = -1;
5158 sheet->active_cell.col = -1;
5159 }
5160
5161 act_row = sheet->active_cell.row;
5162 act_col = sheet->active_cell.col;
5163
5164 GTK_SHEET_ROW_SET_VISIBLE(rowobj, visible);
5165
5166 _gtk_sheet_range_fixup(sheet, &sheet->range);
5167 _gtk_sheet_recalc_top_ypixels(sheet);
5168
5169 _gtk_sheet_scrollbar_adjust(sheet);
5170 _gtk_sheet_redraw_internal(sheet, FALSE, TRUE);
5171 }
5172
5173
5174 /**
5175 * gtk_sheet_get_tooltip_markup:
5176 * @sheet: a #GtkSheet.
5177 *
5178 * Gets the contents of the tooltip (markup) for sheet
5179 *
5180 * Returns: the tooltip text, or NULL. You should free the
5181 * returned string with g_free() when done.
5182 */
gtk_sheet_get_tooltip_markup(GtkSheet * sheet)5183 gchar *gtk_sheet_get_tooltip_markup(GtkSheet *sheet)
5184 {
5185 g_return_val_if_fail(sheet != NULL, NULL);
5186 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
5187
5188 return (gtk_widget_get_tooltip_markup(GTK_WIDGET(sheet)));
5189 }
5190
5191 /**
5192 * gtk_sheet_set_tooltip_markup:
5193 * @sheet: a #GtkSheet.
5194 * @markup: the contents of the tooltip for widget, or NULL.
5195 *
5196 * Sets markup as the contents of the tooltip, which is marked
5197 * up with the Pango text markup language.
5198 */
gtk_sheet_set_tooltip_markup(GtkSheet * sheet,const gchar * markup)5199 void gtk_sheet_set_tooltip_markup(GtkSheet *sheet,
5200 const gchar *markup)
5201 {
5202 g_return_if_fail(sheet != NULL);
5203 g_return_if_fail(GTK_IS_SHEET(sheet));
5204
5205 gtk_widget_set_tooltip_markup(GTK_WIDGET(sheet), markup);
5206 }
5207
5208 /**
5209 * gtk_sheet_get_tooltip_text:
5210 * @sheet: a #GtkSheet.
5211 *
5212 * Gets the contents of the tooltip for the #GtkSheet
5213 *
5214 * Returns: the tooltip text, or NULL. You should free the
5215 * returned string with g_free() when done.
5216 */
gtk_sheet_get_tooltip_text(GtkSheet * sheet)5217 gchar *gtk_sheet_get_tooltip_text(GtkSheet *sheet)
5218 {
5219 g_return_val_if_fail(sheet != NULL, NULL);
5220 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
5221
5222 return (gtk_widget_get_tooltip_text(GTK_WIDGET(sheet)));
5223 }
5224
5225 /**
5226 * gtk_sheet_set_tooltip_text:
5227 * @sheet: a #GtkSheet.
5228 * @text: the contents of the tooltip for widget
5229 *
5230 * Sets text as the contents of the tooltip.
5231 */
gtk_sheet_set_tooltip_text(GtkSheet * sheet,const gchar * text)5232 void gtk_sheet_set_tooltip_text(GtkSheet *sheet,
5233 const gchar *text)
5234 {
5235 g_return_if_fail(sheet != NULL);
5236 g_return_if_fail(GTK_IS_SHEET(sheet));
5237
5238 gtk_widget_set_tooltip_text(GTK_WIDGET(sheet), text);
5239 }
5240
5241 /**
5242 * gtk_sheet_row_get_tooltip_markup:
5243 * @sheet: a #GtkSheet.
5244 * @row: row index
5245 *
5246 * Gets the contents of the tooltip (markup) for the column
5247 *
5248 * Returns: the tooltip text, or NULL. You should free the
5249 * returned string with g_free() when done.
5250 */
gtk_sheet_row_get_tooltip_markup(GtkSheet * sheet,const gint row)5251 gchar *gtk_sheet_row_get_tooltip_markup(GtkSheet *sheet,
5252 const gint row)
5253 {
5254 g_return_val_if_fail(sheet != NULL, NULL);
5255 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
5256
5257 if (row < 0 || row > sheet->maxrow)
5258 return (NULL);
5259
5260 return (g_strdup(ROWPTR(sheet, row)->tooltip_markup));
5261 }
5262
5263 /**
5264 * gtk_sheet_row_set_tooltip_markup:
5265 * @sheet: a #GtkSheet.
5266 * @row: row index
5267 * @markup: the contents of the tooltip for widget, or NULL.
5268 *
5269 * Sets markup as the contents of the tooltip, which is marked
5270 * up with the Pango text markup language.
5271 */
gtk_sheet_row_set_tooltip_markup(GtkSheet * sheet,const gint row,const gchar * markup)5272 void gtk_sheet_row_set_tooltip_markup(GtkSheet *sheet,
5273 const gint row,
5274 const gchar *markup)
5275 {
5276 g_return_if_fail(sheet != NULL);
5277 g_return_if_fail(GTK_IS_SHEET(sheet));
5278
5279 if (row < 0 || row > sheet->maxrow)
5280 return;
5281
5282 if (sheet->row[row].tooltip_markup)
5283 g_free(sheet->row[row].tooltip_markup);
5284 sheet->row[row].tooltip_markup = g_strdup(markup);
5285 }
5286
5287 /**
5288 * gtk_sheet_row_get_tooltip_text:
5289 * @sheet: a #GtkSheet.
5290 * @row: row index
5291 *
5292 * Gets the contents of the tooltip for the column
5293 *
5294 * Returns: the tooltip text, or NULL. You should free the
5295 * returned string with g_free() when done.
5296 */
gtk_sheet_row_get_tooltip_text(GtkSheet * sheet,const gint row)5297 gchar *gtk_sheet_row_get_tooltip_text(GtkSheet *sheet,
5298 const gint row)
5299 {
5300 g_return_val_if_fail(sheet != NULL, NULL);
5301 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
5302
5303 if (row < 0 || row > sheet->maxrow)
5304 return (NULL);
5305
5306 return (g_strdup(ROWPTR(sheet, row)->tooltip_text));
5307 }
5308
5309 /**
5310 * gtk_sheet_row_set_tooltip_text:
5311 * @sheet: a #GtkSheet.
5312 * @row: row index
5313 * @text: the contents of the tooltip for widget
5314 *
5315 * Sets text as the contents of the tooltip.
5316 */
gtk_sheet_row_set_tooltip_text(GtkSheet * sheet,const gint row,const gchar * text)5317 void gtk_sheet_row_set_tooltip_text(GtkSheet *sheet,
5318 const gint row,
5319 const gchar *text)
5320 {
5321 g_return_if_fail(sheet != NULL);
5322 g_return_if_fail(GTK_IS_SHEET(sheet));
5323
5324 if (row < 0 || row > sheet->maxrow)
5325 return;
5326
5327 if (sheet->row[row].tooltip_text)
5328 g_free(sheet->row[row].tooltip_text);
5329 sheet->row[row].tooltip_text = g_strdup(text);
5330 }
5331
5332 /**
5333 * gtk_sheet_row_get_readonly:
5334 * @sheet: a #GtkSheet.
5335 * @row: row index
5336 *
5337 * Gets the row readonly flag
5338 *
5339 * Returns: the readonly flag
5340 */
gtk_sheet_row_get_readonly(GtkSheet * sheet,const gint row)5341 gboolean gtk_sheet_row_get_readonly(GtkSheet *sheet, const gint row)
5342 {
5343 g_return_val_if_fail(sheet != NULL, FALSE);
5344 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5345
5346 if (row < 0 || row > sheet->maxrow) return (FALSE);
5347
5348 return (ROWPTR(sheet, row)->is_readonly);
5349 }
5350
5351 /**
5352 * gtk_sheet_row_set_readonly:
5353 * @sheet: a #GtkSheet.
5354 * @row: row index
5355 * @is_readonly: the row is_readonly flag
5356 *
5357 * Sets the row readonly flag.
5358 * A cell is editable if the sheet is not locked, the row is
5359 * not readonly and the cell (-range) was set to editable.
5360 */
gtk_sheet_row_set_readonly(GtkSheet * sheet,const gint row,const gboolean is_readonly)5361 void gtk_sheet_row_set_readonly(GtkSheet *sheet, const gint row,
5362 const gboolean is_readonly)
5363 {
5364 g_return_if_fail(sheet != NULL);
5365 g_return_if_fail(GTK_IS_SHEET(sheet));
5366
5367 if (row < 0 || row > sheet->maxrow) return;
5368
5369 ROWPTR(sheet, row)->is_readonly = is_readonly;
5370 }
5371
5372 /**
5373 * gtk_sheet_row_get_can_focus:
5374 * @sheet: a #GtkSheet.
5375 * @row: row index
5376 *
5377 * Gets the row can_focus flag
5378 *
5379 * Returns: the can_focus flag
5380 */
gtk_sheet_row_get_can_focus(GtkSheet * sheet,const gint row)5381 gboolean gtk_sheet_row_get_can_focus(GtkSheet *sheet, const gint row)
5382 {
5383 g_return_val_if_fail(sheet != NULL, FALSE);
5384 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5385
5386 if (row < 0 || row > sheet->maxrow) return (FALSE);
5387
5388 return (ROWPTR(sheet, row)->can_focus);
5389 }
5390
5391 /**
5392 * gtk_sheet_row_set_can_focus:
5393 * @sheet: a #GtkSheet.
5394 * @row: row index
5395 * @can_focus: the row can_focus flag
5396 *
5397 * Sets the row can_focus flag.
5398 * Note: Does not check row visiblity, sensitivity, etc. Use the
5399 * macro GTK_SHEET_ROW_CAN_GRAB_FOCUS() for dependency checking.
5400 */
gtk_sheet_row_set_can_focus(GtkSheet * sheet,const gint row,const gboolean can_focus)5401 void gtk_sheet_row_set_can_focus(GtkSheet *sheet, const gint row,
5402 const gboolean can_focus)
5403 {
5404 g_return_if_fail(sheet != NULL);
5405 g_return_if_fail(GTK_IS_SHEET(sheet));
5406
5407 if (row < 0 || row > sheet->maxrow) return;
5408
5409 ROWPTR(sheet, row)->can_focus = can_focus;
5410 }
5411
5412 /**
5413 * gtk_sheet_cell_get_tooltip_markup:
5414 * @sheet: a #GtkSheet.
5415 * @row: row index
5416 * @col: column index
5417 *
5418 * Gets the contents of the tooltip (markup) for the column
5419 *
5420 * Returns: the tooltip text, or NULL. You should free the
5421 * returned string with g_free() when done.
5422 */
gtk_sheet_cell_get_tooltip_markup(GtkSheet * sheet,const gint row,const gint col)5423 gchar *gtk_sheet_cell_get_tooltip_markup(GtkSheet *sheet,
5424 const gint row, const gint col)
5425 {
5426 g_return_val_if_fail(sheet != NULL, NULL);
5427 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
5428
5429 if (col < 0 || col > sheet->maxcol)
5430 return (NULL);
5431 if (row < 0 || row > sheet->maxrow)
5432 return (NULL);
5433
5434 if (row > sheet->maxallocrow || col > sheet->maxalloccol)
5435 return (NULL);
5436
5437 if (!sheet->data[row])
5438 return (NULL);
5439 if (!sheet->data[row][col])
5440 return (NULL);
5441
5442 return (g_strdup(sheet->data[row][col]->tooltip_markup));
5443 }
5444
5445 /**
5446 * gtk_sheet_cell_set_tooltip_markup:
5447 * @sheet: a #GtkSheet.
5448 * @row: row index
5449 * @col: column index
5450 * @markup: the contents of the tooltip for widget, or NULL.
5451 *
5452 * Sets markup as the contents of the tooltip, which is marked
5453 * up with the Pango text markup language.
5454 */
gtk_sheet_cell_set_tooltip_markup(GtkSheet * sheet,const gint row,const gint col,const gchar * markup)5455 void gtk_sheet_cell_set_tooltip_markup(GtkSheet *sheet,
5456 const gint row, const gint col,
5457 const gchar *markup)
5458 {
5459 GtkSheetCell *cell;
5460
5461 g_return_if_fail(sheet != NULL);
5462 g_return_if_fail(GTK_IS_SHEET(sheet));
5463
5464 if (col < 0 || col > sheet->maxcol)
5465 return;
5466 if (row < 0 || row > sheet->maxrow)
5467 return;
5468
5469 CheckCellData(sheet, row, col);
5470 cell = sheet->data[row][col];
5471
5472 if (cell->tooltip_markup)
5473 {
5474 g_free(cell->tooltip_markup);
5475 cell->tooltip_markup = NULL;
5476 }
5477
5478 cell->tooltip_markup = g_strdup(markup);
5479 }
5480
5481 /**
5482 * gtk_sheet_cell_get_tooltip_text:
5483 * @sheet: a #GtkSheet.
5484 * @row: row index
5485 * @col: column index
5486 *
5487 * Gets the contents of the tooltip for the column
5488 *
5489 * Returns: the tooltip text, or NULL. You should free the
5490 * returned string with g_free() when done.
5491 */
gtk_sheet_cell_get_tooltip_text(GtkSheet * sheet,const gint row,const gint col)5492 gchar *gtk_sheet_cell_get_tooltip_text(GtkSheet *sheet,
5493 const gint row, const gint col)
5494 {
5495 g_return_val_if_fail(sheet != NULL, NULL);
5496 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
5497
5498 if (col < 0 || col > sheet->maxcol)
5499 return (NULL);
5500 if (row < 0 || row > sheet->maxrow)
5501 return (NULL);
5502
5503 if (row > sheet->maxallocrow || col > sheet->maxalloccol)
5504 return (NULL);
5505
5506 if (!sheet->data[row])
5507 return (NULL);
5508 if (!sheet->data[row][col])
5509 return (NULL);
5510
5511 return (g_strdup(sheet->data[row][col]->tooltip_text));
5512 }
5513
5514 /**
5515 * gtk_sheet_cell_set_tooltip_text:
5516 * @sheet: a #GtkSheet.
5517 * @row: row index
5518 * @col: column index
5519 * @text: the contents of the tooltip for widget
5520 *
5521 * Sets text as the contents of the tooltip.
5522 */
gtk_sheet_cell_set_tooltip_text(GtkSheet * sheet,const gint row,const gint col,const gchar * text)5523 void gtk_sheet_cell_set_tooltip_text(GtkSheet *sheet,
5524 const gint row, const gint col,
5525 const gchar *text)
5526 {
5527 GtkSheetCell *cell;
5528
5529 g_return_if_fail(sheet != NULL);
5530 g_return_if_fail(GTK_IS_SHEET(sheet));
5531
5532 if (col < 0 || col > sheet->maxcol)
5533 return;
5534 if (row < 0 || row > sheet->maxrow)
5535 return;
5536
5537 CheckCellData(sheet, row, col);
5538 cell = sheet->data[row][col];
5539
5540 if (cell->tooltip_text)
5541 {
5542 g_free(cell->tooltip_text);
5543 cell->tooltip_text = NULL;
5544 }
5545
5546 cell->tooltip_text = g_strdup(text);
5547 }
5548
5549 /**
5550 * gtk_sheet_cell_get_editable:
5551 * @sheet: a #GtkSheet.
5552 * @row: row index
5553 * @col: column index
5554 *
5555 * Check editable status of sheet, row, column and cell to decide
5556 * if a sheet cell is editable
5557 *
5558 * Returns: TRUE if editable, FALSE if blocked by any level.
5559 * NOTE: this routine also checks if the sheet row/column/cell
5560 * can receive focus. A cell may be editable, but not focusable
5561 * still rendering it unable to be changed by the user.
5562 *
5563 * To check only the editable attribute on the cell,
5564 * use #gtk_sheet_get_attributes() to fetch cell attributes and
5565 * examine them.
5566 */
gtk_sheet_cell_get_editable(GtkSheet * sheet,const gint row,const gint col)5567 gboolean gtk_sheet_cell_get_editable(GtkSheet *sheet,
5568 const gint row, const gint col)
5569 {
5570 g_return_val_if_fail(sheet != NULL, FALSE);
5571 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5572
5573 if (col < 0 || col > sheet->maxcol)
5574 return (FALSE);
5575 if (row < 0 || row > sheet->maxrow)
5576 return (FALSE);
5577
5578 GtkSheetColumn *colptr = COLPTR(sheet,col);
5579 GtkSheetRow *rowptr = ROWPTR(sheet,row);
5580 GtkSheetCellAttr myattr;
5581
5582 /* if the sheet is locked, or the row/col are
5583 * readonly, then the cell is automatically
5584 * not editable, even if its local edit flag allows it.
5585 * NOTE: the cell may be still not visible if the row/col
5586 * is marked as not visible.
5587 */
5588 if( GTK_SHEET_IS_LOCKED(sheet)
5589 || GTK_SHEET_ROW_IS_READONLY(rowptr)
5590 || GTK_SHEET_COLUMN_IS_READONLY(colptr)
5591 || !GTK_SHEET_ROW_CAN_FOCUS(rowptr)
5592 || !GTK_SHEET_COLUMN_CAN_FOCUS(colptr) )
5593 return FALSE;
5594
5595 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5596 if( !myattr.is_editable || !myattr.can_focus )
5597 return FALSE;
5598
5599 return TRUE;
5600 }
5601
5602 /**
5603 * gtk_sheet_cell_set_editable:
5604 * @sheet: a #GtkSheet.
5605 * @row: row index
5606 * @col: column index
5607 * @is_editable: value for the editable status of the cell
5608 *
5609 * Sets cell editable status in cell attributes.
5610 */
gtk_sheet_cell_set_editable(GtkSheet * sheet,const gint row,const gint col,const gboolean is_editable)5611 void gtk_sheet_cell_set_editable(GtkSheet *sheet,
5612 const gint row, const gint col,
5613 const gboolean is_editable)
5614 {
5615 GtkSheetCellAttr myattr;
5616
5617 g_return_if_fail(sheet != NULL);
5618 g_return_if_fail(GTK_IS_SHEET(sheet));
5619
5620 if (col < 0 || col > sheet->maxcol)
5621 return;
5622 if (row < 0 || row > sheet->maxrow)
5623 return;
5624
5625 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5626 myattr.is_editable = is_editable;
5627 gtk_sheet_set_cell_attributes(sheet,row,col,myattr);
5628 return;
5629 }
5630
5631 /**
5632 * gtk_sheet_cell_get_sensitive
5633 * @sheet: a #GtkSheet.
5634 * @row: row index
5635 * @col: column index
5636 *
5637 * Check sensitivity status of sheet, row, column and cell to decide
5638 * if a sheet cell can receive focus.
5639 *
5640 * Returns: TRUE if cell is not sensitive, FALSE if it is marked
5641 * sensitive at any level.
5642 */
gtk_sheet_cell_get_sensitive(GtkSheet * sheet,const gint row,const gint col)5643 gboolean gtk_sheet_cell_get_sensitive(GtkSheet *sheet,
5644 const gint row, const gint col)
5645 {
5646 g_return_val_if_fail(sheet != NULL, FALSE);
5647 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5648
5649 if (col < 0 || col > sheet->maxcol)
5650 return (FALSE);
5651 if (row < 0 || row > sheet->maxrow)
5652 return (FALSE);
5653
5654 GtkSheetColumn *colptr = COLPTR(sheet,col);
5655 GtkSheetRow *rowptr = ROWPTR(sheet,row);
5656 GtkSheetCellAttr myattr;
5657
5658 /* if the widget/row/col are insensitive, then the
5659 * cell is automatically not sensitive, even if
5660 * its local flag allows it.
5661 */
5662 if(!gtk_widget_get_sensitive(GTK_WIDGET(sheet))
5663 || !GTK_SHEET_ROW_IS_SENSITIVE(rowptr)
5664 || !GTK_SHEET_COLUMN_IS_SENSITIVE(colptr) )
5665 return FALSE;
5666
5667 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5668 if( !myattr.is_sensitive )
5669 return FALSE;
5670
5671 return TRUE;
5672 }
5673
5674 /**
5675 * gtk_sheet_cell_set_sensitive:
5676 * @sheet: a #GtkSheet.
5677 * @row: row index
5678 * @col: column index
5679 * @is_sensitive: value for the sensitive status of the cell
5680 *
5681 * Sets cell sensitive status
5682 */
gtk_sheet_cell_set_sensitive(GtkSheet * sheet,const gint row,const gint col,const gboolean is_sensitive)5683 void gtk_sheet_cell_set_sensitive(GtkSheet *sheet,
5684 const gint row, const gint col,
5685 const gboolean is_sensitive)
5686 {
5687 GtkSheetCellAttr myattr;
5688
5689 g_return_if_fail(sheet != NULL);
5690 g_return_if_fail(GTK_IS_SHEET(sheet));
5691
5692 if (col < 0 || col > sheet->maxcol)
5693 return;
5694 if (row < 0 || row > sheet->maxrow)
5695 return;
5696
5697 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5698 myattr.is_sensitive = is_sensitive;
5699 gtk_sheet_set_cell_attributes(sheet,row,col,myattr);
5700 return;
5701 }
5702
5703
5704 /**
5705 * gtk_sheet_cell_get_can_focus:
5706 * @sheet: a #GtkSheet.
5707 * @row: row index
5708 * @col: column index
5709 *
5710 * Check ability of cell to grab focus. Check includes
5711 * checking for blocks at row/column/sheet level.
5712 *
5713 * Returns: TRUE if the cell can focus, FALSE if blocked by any level
5714 */
gtk_sheet_cell_get_can_focus(GtkSheet * sheet,const gint row,const gint col)5715 gboolean gtk_sheet_cell_get_can_focus(GtkSheet *sheet,
5716 const gint row, const gint col)
5717 {
5718 g_return_val_if_fail(sheet != NULL, FALSE);
5719 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5720
5721 if (col < 0 || col > sheet->maxcol)
5722 return (FALSE);
5723 if (row < 0 || row > sheet->maxrow)
5724 return (FALSE);
5725
5726 GtkSheetColumn *colptr = COLPTR(sheet,col);
5727 GtkSheetRow *rowptr = ROWPTR(sheet,row);
5728 GtkSheetCellAttr myattr;
5729
5730 /* if the sheet row/col are invisible, insensitive or not
5731 * allowing focus, then the cell is automatically
5732 * not focusable, even if its local edit flag allows it.
5733 */
5734 if(!GTK_SHEET_ROW_CAN_GRAB_FOCUS(rowptr)
5735 || !GTK_SHEET_COLUMN_CAN_GRAB_FOCUS(colptr) )
5736 return FALSE;
5737
5738 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5739 if( !myattr.can_focus )
5740 return FALSE;
5741
5742 return TRUE;
5743 }
5744
5745 /**
5746 * gtk_sheet_cell_set_can_focus:
5747 * @sheet: a #GtkSheet.
5748 * @row: row index
5749 * @col: column index
5750 * @can_focus: value for the editable status of the cell
5751 *
5752 * Sets cell can_focus flag
5753 */
gtk_sheet_cell_set_can_focus(GtkSheet * sheet,const gint row,const gint col,const gboolean can_focus)5754 void gtk_sheet_cell_set_can_focus(GtkSheet *sheet,
5755 const gint row, const gint col,
5756 const gboolean can_focus)
5757 {
5758 GtkSheetCellAttr myattr;
5759
5760 g_return_if_fail(sheet != NULL);
5761 g_return_if_fail(GTK_IS_SHEET(sheet));
5762
5763 if (col < 0 || col > sheet->maxcol)
5764 return;
5765 if (row < 0 || row > sheet->maxrow)
5766 return;
5767
5768 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5769 myattr.can_focus = can_focus;
5770 gtk_sheet_set_cell_attributes(sheet,row,col,myattr);
5771 return;
5772 }
5773
5774 /**
5775 * gtk_sheet_cell_get_visible:
5776 * @sheet: a #GtkSheet.
5777 * @row: row index
5778 * @col: column index
5779 *
5780 * Check visiblity of cell.
5781 *
5782 * Returns: TRUE if the cell is visible, FALSE otherwise.
5783 */
gtk_sheet_cell_get_visible(GtkSheet * sheet,const gint row,const gint col)5784 gboolean gtk_sheet_cell_get_visible(GtkSheet *sheet,
5785 const gint row, const gint col)
5786 {
5787 g_return_val_if_fail(sheet != NULL, FALSE);
5788 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5789
5790 if (col < 0 || col > sheet->maxcol)
5791 return (FALSE);
5792 if (row < 0 || row > sheet->maxrow)
5793 return (FALSE);
5794
5795 GtkSheetColumn *colptr = COLPTR(sheet,col);
5796 GtkSheetRow *rowptr = ROWPTR(sheet,row);
5797 GtkSheetCellAttr myattr;
5798
5799 /* if the sheet row/col are invisible,
5800 * then so is the cell, even if its local flag allows it.
5801 */
5802 if(!GTK_SHEET_ROW_IS_VISIBLE(rowptr)
5803 || !GTK_SHEET_COLUMN_IS_VISIBLE(colptr) )
5804 return FALSE;
5805
5806 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5807 if( !myattr.is_visible )
5808 return FALSE;
5809
5810 return TRUE;
5811 }
5812
5813 /**
5814 * gtk_sheet_cell_set_visible:
5815 * @sheet: a #GtkSheet.
5816 * @row: row index
5817 * @col: column index
5818 * @is_visible: value for the visibility status of the cell
5819 *
5820 * Sets cell is_visible flag
5821 */
gtk_sheet_cell_set_visible(GtkSheet * sheet,const gint row,const gint col,const gboolean is_visible)5822 void gtk_sheet_cell_set_visible(GtkSheet *sheet,
5823 const gint row, const gint col,
5824 const gboolean is_visible)
5825 {
5826 GtkSheetCellAttr myattr;
5827
5828 g_return_if_fail(sheet != NULL);
5829 g_return_if_fail(GTK_IS_SHEET(sheet));
5830
5831 if (col < 0 || col > sheet->maxcol)
5832 return;
5833 if (row < 0 || row > sheet->maxrow)
5834 return;
5835
5836 gtk_sheet_get_attributes(sheet,row,col,&myattr);
5837 myattr.is_visible = is_visible;
5838 gtk_sheet_set_cell_attributes(sheet,row,col,myattr);
5839 return;
5840 }
5841
5842 /**
5843 * gtk_sheet_select_row:
5844 * @sheet: a #GtkSheet.
5845 * @row: row number
5846 *
5847 * Select the row. The range is then highlighted, and the bounds are stored in sheet->range.
5848 */
5849 void
gtk_sheet_select_row(GtkSheet * sheet,gint row)5850 gtk_sheet_select_row(GtkSheet *sheet, gint row)
5851 {
5852 g_return_if_fail(sheet != NULL);
5853 g_return_if_fail(GTK_IS_SHEET(sheet));
5854
5855 if (row < 0 || row > sheet->maxrow)
5856 return;
5857
5858 if (sheet->state != GTK_SHEET_NORMAL)
5859 {
5860 gtk_sheet_real_unselect_range(sheet, NULL);
5861 }
5862 else
5863 {
5864 gboolean veto = TRUE;
5865 veto = gtk_sheet_deactivate_cell(sheet);
5866 if (!veto)
5867 return;
5868 }
5869
5870 sheet->state = GTK_SHEET_ROW_SELECTED;
5871
5872 sheet->range.row0 = row;
5873 sheet->range.col0 = 0;
5874 sheet->range.rowi = row;
5875 sheet->range.coli = sheet->maxcol;
5876
5877 sheet->active_cell.row = row;
5878 sheet->active_cell.col = 0;
5879
5880 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[SELECT_ROW], 0, row);
5881 gtk_sheet_real_select_range(sheet, NULL);
5882 }
5883
5884 /**
5885 * gtk_sheet_select_column:
5886 * @sheet: a #GtkSheet.
5887 * @column: column number
5888 *
5889 * Select the column. The range is then highlighted, and the bounds are stored in sheet->range.
5890 */
5891 void
gtk_sheet_select_column(GtkSheet * sheet,gint column)5892 gtk_sheet_select_column(GtkSheet *sheet, gint column)
5893 {
5894 g_return_if_fail(sheet != NULL);
5895 g_return_if_fail(GTK_IS_SHEET(sheet));
5896
5897 if (column < 0 || column > sheet->maxcol)
5898 return;
5899
5900 if (sheet->state != GTK_SHEET_NORMAL)
5901 {
5902 gtk_sheet_real_unselect_range(sheet, NULL);
5903 }
5904 else
5905 {
5906 gboolean veto = TRUE;
5907 veto = gtk_sheet_deactivate_cell(sheet);
5908 if (!veto)
5909 return;
5910 }
5911
5912 sheet->state = GTK_SHEET_COLUMN_SELECTED;
5913
5914 sheet->range.row0 = 0;
5915 sheet->range.col0 = column;
5916 sheet->range.rowi = sheet->maxrow;
5917 sheet->range.coli = column;
5918
5919 sheet->active_cell.row = 0;
5920 sheet->active_cell.col = column;
5921
5922 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[SELECT_COLUMN], 0, column);
5923 gtk_sheet_real_select_range(sheet, NULL);
5924 }
5925
5926 /**
5927 * gtk_sheet_clip_range:
5928 * @sheet: a #GtkSheet.
5929 * @clip_range: #GtkSheetRange to be saved
5930 *
5931 * Save selected range to "clipboard".
5932 */
5933 void
gtk_sheet_clip_range(GtkSheet * sheet,const GtkSheetRange * clip_range)5934 gtk_sheet_clip_range(GtkSheet *sheet, const GtkSheetRange *clip_range)
5935 {
5936 g_return_if_fail(sheet != NULL);
5937 g_return_if_fail(GTK_IS_SHEET(sheet));
5938
5939 if (GTK_SHEET_IN_CLIP(sheet))
5940 return;
5941
5942 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_CLIP);
5943
5944 if (clip_range == NULL)
5945 sheet->clip_range = sheet->range;
5946 else
5947 sheet->clip_range = *clip_range;
5948
5949 sheet->interval = 0;
5950 sheet->clip_timer = g_timeout_add_full(0, TIMEOUT_FLASH, gtk_sheet_flash, sheet, NULL);
5951
5952 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[CLIP_RANGE], 0, &sheet->clip_range);
5953 }
5954
5955 /**
5956 * gtk_sheet_unclip_range:
5957 * @sheet: a #GtkSheet.
5958 *
5959 * Free clipboard.
5960 */
5961 void
gtk_sheet_unclip_range(GtkSheet * sheet)5962 gtk_sheet_unclip_range(GtkSheet *sheet)
5963 {
5964 g_return_if_fail(sheet != NULL);
5965 g_return_if_fail(GTK_IS_SHEET(sheet));
5966
5967 if (!GTK_SHEET_IN_CLIP(sheet))
5968 return;
5969
5970 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_CLIP);
5971 g_source_remove(sheet->clip_timer);
5972 _gtk_sheet_range_draw(sheet, &sheet->clip_range, TRUE);
5973
5974 if (gtk_sheet_range_isvisible(sheet, sheet->range))
5975 _gtk_sheet_range_draw(sheet, &sheet->range, TRUE);
5976 }
5977
5978 /**
5979 * gtk_sheet_in_clip:
5980 * @sheet: a #GtkSheet.
5981 *
5982 * Get the clip status of #GtkSheet.
5983 *
5984 * Returns: TRUE or FALSE
5985 */
5986 gboolean
gtk_sheet_in_clip(GtkSheet * sheet)5987 gtk_sheet_in_clip(GtkSheet *sheet)
5988 {
5989 g_return_val_if_fail(sheet != NULL, FALSE);
5990 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
5991
5992 return (GTK_SHEET_IN_CLIP(sheet));
5993 }
5994
5995
5996 static gint
gtk_sheet_flash(gpointer data)5997 gtk_sheet_flash(gpointer data)
5998 {
5999 GtkSheet *sheet;
6000 gint x, y, width, height;
6001 GdkRectangle clip_area;
6002
6003 sheet = GTK_SHEET(data);
6004
6005 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
6006 return (TRUE);
6007 if (!gtk_widget_is_drawable(GTK_WIDGET(sheet)))
6008 return (TRUE);
6009 if (!gtk_sheet_range_isvisible(sheet, sheet->clip_range))
6010 return (TRUE);
6011 if (GTK_SHEET_IN_XDRAG(sheet))
6012 return (TRUE);
6013 if (GTK_SHEET_IN_YDRAG(sheet))
6014 return (TRUE);
6015
6016 GDK_THREADS_ENTER();
6017
6018 x = _gtk_sheet_column_left_xpixel(sheet, sheet->clip_range.col0) + 1;
6019 y = _gtk_sheet_row_top_ypixel(sheet, sheet->clip_range.row0) + 1;
6020 width = _gtk_sheet_column_left_xpixel(sheet, sheet->clip_range.coli) - x +
6021 COLPTR(sheet, sheet->clip_range.coli)->width - 1;
6022 height = _gtk_sheet_row_top_ypixel(sheet, sheet->clip_range.rowi) - y +
6023 sheet->row[sheet->clip_range.rowi].height - 1;
6024
6025 clip_area.x = _gtk_sheet_column_left_xpixel(sheet, MIN_VIEW_COLUMN(sheet));
6026 clip_area.y = _gtk_sheet_row_top_ypixel(sheet, MIN_VIEW_ROW(sheet));
6027 clip_area.width = sheet->sheet_window_width;
6028 clip_area.height = sheet->sheet_window_height;
6029
6030 if (x < 0)
6031 {
6032 width = width + x + 1;
6033 x = -1;
6034 }
6035 if (width > clip_area.width)
6036 width = clip_area.width + 10;
6037
6038 if (y < 0)
6039 {
6040 height = height + y + 1;
6041 y = -1;
6042 }
6043 if (height > clip_area.height)
6044 height = clip_area.height + 10;
6045
6046 gdk_draw_pixmap(sheet->sheet_window,
6047 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
6048 sheet->pixmap,
6049 x, y,
6050 x, y,
6051 1, height);
6052
6053 gdk_draw_pixmap(sheet->sheet_window,
6054 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
6055 sheet->pixmap,
6056 x, y,
6057 x, y,
6058 width, 1);
6059
6060 gdk_draw_pixmap(sheet->sheet_window,
6061 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
6062 sheet->pixmap,
6063 x, y + height,
6064 x, y + height,
6065 width, 1);
6066
6067 gdk_draw_pixmap(sheet->sheet_window,
6068 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
6069 sheet->pixmap,
6070 x + width, y,
6071 x + width, y,
6072 1, height);
6073
6074 sheet->interval = sheet->interval + 1;
6075 if (sheet->interval == TIME_INTERVAL)
6076 sheet->interval = 0;
6077
6078 gdk_gc_set_dashes(sheet->xor_gc, sheet->interval, (gint8 *)"\4\4", 2);
6079 gtk_sheet_draw_flashing_range(sheet, sheet->clip_range);
6080 gdk_gc_set_dashes(sheet->xor_gc, 0, (gint8 *)"\4\4", 2);
6081
6082 GDK_THREADS_LEAVE();
6083
6084 return (TRUE);
6085 }
6086
6087 static void
gtk_sheet_draw_flashing_range(GtkSheet * sheet,GtkSheetRange range)6088 gtk_sheet_draw_flashing_range(GtkSheet *sheet, GtkSheetRange range)
6089 {
6090 GdkRectangle clip_area;
6091 gint x, y, width, height;
6092
6093 if (!gtk_sheet_range_isvisible(sheet, sheet->clip_range))
6094 return;
6095
6096 clip_area.x = _gtk_sheet_column_left_xpixel(sheet, MIN_VIEW_COLUMN(sheet));
6097 clip_area.y = _gtk_sheet_row_top_ypixel(sheet, MIN_VIEW_ROW(sheet));
6098 clip_area.width = sheet->sheet_window_width;
6099 clip_area.height = sheet->sheet_window_height;
6100
6101 gdk_gc_set_clip_rectangle(sheet->xor_gc, &clip_area);
6102
6103 x = _gtk_sheet_column_left_xpixel(sheet, sheet->clip_range.col0) + 1;
6104 y = _gtk_sheet_row_top_ypixel(sheet, sheet->clip_range.row0) + 1;
6105 width = _gtk_sheet_column_left_xpixel(sheet, sheet->clip_range.coli) - x +
6106 COLPTR(sheet, sheet->clip_range.coli)->width - 1;
6107 height = _gtk_sheet_row_top_ypixel(sheet, sheet->clip_range.rowi) - y +
6108 sheet->row[sheet->clip_range.rowi].height - 1;
6109
6110 if (x < 0)
6111 {
6112 width = width + x + 1;
6113 x = -1;
6114 }
6115 if (width > clip_area.width)
6116 width = clip_area.width + 10;
6117
6118 if (y < 0)
6119 {
6120 height = height + y + 1;
6121 y = -1;
6122 }
6123 if (height > clip_area.height)
6124 height = clip_area.height + 10;
6125
6126 gdk_gc_set_line_attributes(sheet->xor_gc, 1, 1, 0, 0);
6127
6128 gdk_draw_rectangle(sheet->sheet_window, sheet->xor_gc, FALSE,
6129 x, y,
6130 width, height);
6131
6132 gdk_gc_set_line_attributes(sheet->xor_gc, 1, 0, 0, 0);
6133
6134 gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL);
6135 }
6136
6137 static gint
gtk_sheet_range_isvisible(GtkSheet * sheet,GtkSheetRange range)6138 gtk_sheet_range_isvisible(GtkSheet *sheet, GtkSheetRange range)
6139 {
6140 g_return_val_if_fail(sheet != NULL, FALSE);
6141
6142 if (range.row0 > MAX_VIEW_ROW(sheet))
6143 return (FALSE);
6144 if (range.rowi < MIN_VIEW_ROW(sheet))
6145 return (FALSE);
6146
6147 if (range.col0 > MAX_VIEW_COLUMN(sheet))
6148 return (FALSE);
6149 if (range.coli < MIN_VIEW_COLUMN(sheet))
6150 return (FALSE);
6151
6152 return (TRUE);
6153 }
6154
6155 static gint
gtk_sheet_cell_isvisible(GtkSheet * sheet,gint row,gint column)6156 gtk_sheet_cell_isvisible(GtkSheet *sheet,
6157 gint row, gint column)
6158 {
6159 GtkSheetRange range;
6160
6161 range.row0 = row;
6162 range.col0 = column;
6163 range.rowi = row;
6164 range.coli = column;
6165
6166 return (gtk_sheet_range_isvisible(sheet, range));
6167 }
6168
6169 /**
6170 * gtk_sheet_get_visible_range:
6171 * @sheet: a #GtkSheet.
6172 * @range: a selected #GtkSheetRange
6173 * struct _GtkSheetRange { gint row0,col0; // upper-left cell
6174 * gint rowi,coli; // lower-right cell };
6175 *
6176 * Get sheet's ranges in a #GkSheetRange structure.
6177 */
6178 void
gtk_sheet_get_visible_range(GtkSheet * sheet,GtkSheetRange * range)6179 gtk_sheet_get_visible_range(GtkSheet *sheet, GtkSheetRange *range)
6180 {
6181 g_return_if_fail(sheet != NULL);
6182 g_return_if_fail(GTK_IS_SHEET(sheet));
6183 g_return_if_fail(range != NULL);
6184
6185 range->row0 = MIN_VIEW_ROW(sheet);
6186 range->col0 = MIN_VIEW_COLUMN(sheet);
6187 range->rowi = MAX_VIEW_ROW(sheet);
6188 range->coli = MAX_VIEW_COLUMN(sheet);
6189 }
6190
6191 /**
6192 * gtk_sheet_get_vadjustment:
6193 * @sheet: a #GtkSheet.
6194 *
6195 * Get vertical scroll adjustments.
6196 *
6197 * Returns: (transfer none) a #GtkAdjustment
6198 */
6199 GtkAdjustment *
gtk_sheet_get_vadjustment(GtkSheet * sheet)6200 gtk_sheet_get_vadjustment(GtkSheet *sheet)
6201 {
6202 g_return_val_if_fail(sheet != NULL, NULL);
6203 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
6204
6205 return (sheet->vadjustment);
6206 }
6207
6208 /**
6209 * gtk_sheet_get_hadjustment:
6210 * @sheet: a #GtkSheet.
6211 *
6212 * Get horizontal scroll adjustments.
6213 *
6214 * Returns: (transfer none) a #GtkAdjustment
6215 */
6216 GtkAdjustment *
gtk_sheet_get_hadjustment(GtkSheet * sheet)6217 gtk_sheet_get_hadjustment(GtkSheet *sheet)
6218 {
6219 g_return_val_if_fail(sheet != NULL, NULL);
6220 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
6221
6222 return (sheet->hadjustment);
6223 }
6224
6225 /**
6226 * gtk_sheet_set_vadjustment:
6227 * @sheet: a #GtkSheet.
6228 * @adjustment: a #GtkAdjustment
6229 *
6230 * Change vertical scroll adjustments.
6231 */
6232 void
gtk_sheet_set_vadjustment(GtkSheet * sheet,GtkAdjustment * adjustment)6233 gtk_sheet_set_vadjustment(GtkSheet *sheet, GtkAdjustment *adjustment)
6234 {
6235 GtkAdjustment *old_adjustment;
6236
6237 g_return_if_fail(sheet != NULL);
6238 g_return_if_fail(GTK_IS_SHEET(sheet));
6239
6240 if (adjustment)
6241 g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
6242 if (sheet->vadjustment == adjustment)
6243 return;
6244
6245 old_adjustment = sheet->vadjustment;
6246
6247 if (sheet->vadjustment)
6248 {
6249 g_signal_handlers_disconnect_matched(
6250 GTK_OBJECT(sheet->vadjustment),
6251 G_SIGNAL_MATCH_DATA,
6252 0, 0, NULL, NULL, sheet);
6253 g_object_unref(G_OBJECT(sheet->vadjustment));
6254 }
6255
6256 sheet->vadjustment = adjustment;
6257
6258 if (sheet->vadjustment)
6259 {
6260 g_object_ref(G_OBJECT(sheet->vadjustment));
6261 g_object_ref_sink(G_OBJECT(sheet->vadjustment));
6262 g_object_unref(G_OBJECT(sheet->vadjustment));
6263
6264 g_signal_connect(GTK_OBJECT(sheet->vadjustment), "changed",
6265 (void *)_vadjustment_changed_handler,
6266 (gpointer)sheet);
6267 g_signal_connect(GTK_OBJECT(sheet->vadjustment), "value_changed",
6268 (void *)_vadjustment_value_changed_handler,
6269 (gpointer)sheet);
6270 }
6271
6272 if (!sheet->vadjustment || !old_adjustment)
6273 {
6274 gtk_widget_queue_resize(GTK_WIDGET(sheet));
6275 return;
6276 }
6277
6278 sheet->old_vadjustment = gtk_adjustment_get_value(sheet->vadjustment);
6279 }
6280
6281 /**
6282 * gtk_sheet_set_hadjustment:
6283 * @sheet: a #GtkSheet.
6284 * @adjustment: a #GtkAdjustment
6285 *
6286 * Change horizontal scroll adjustments.
6287 */
6288 void
gtk_sheet_set_hadjustment(GtkSheet * sheet,GtkAdjustment * adjustment)6289 gtk_sheet_set_hadjustment(GtkSheet *sheet, GtkAdjustment *adjustment)
6290 {
6291 GtkAdjustment *old_adjustment;
6292
6293 g_return_if_fail(sheet != NULL);
6294 g_return_if_fail(GTK_IS_SHEET(sheet));
6295
6296 if (adjustment)
6297 g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment));
6298 if (sheet->hadjustment == adjustment)
6299 return;
6300
6301 old_adjustment = sheet->hadjustment;
6302
6303 if (sheet->hadjustment)
6304 {
6305 g_signal_handlers_disconnect_matched(
6306 GTK_OBJECT(sheet->hadjustment),
6307 G_SIGNAL_MATCH_DATA,
6308 0, 0, NULL, NULL, sheet);
6309 g_object_unref(GTK_OBJECT(sheet->hadjustment));
6310 }
6311
6312 sheet->hadjustment = adjustment;
6313
6314 if (sheet->hadjustment)
6315 {
6316 g_object_ref(G_OBJECT(sheet->hadjustment));
6317 g_object_ref_sink(G_OBJECT(sheet->hadjustment));
6318 g_object_unref(G_OBJECT(sheet->hadjustment));
6319
6320 g_signal_connect(GTK_OBJECT(sheet->hadjustment), "changed",
6321 (void *)_hadjustment_changed_handler,
6322 (gpointer)sheet);
6323 g_signal_connect(GTK_OBJECT(sheet->hadjustment), "value_changed",
6324 (void *)_hadjustment_value_changed_handler,
6325 (gpointer)sheet);
6326 }
6327
6328 if (!sheet->hadjustment || !old_adjustment)
6329 {
6330 gtk_widget_queue_resize(GTK_WIDGET(sheet));
6331 return;
6332 }
6333
6334 sheet->old_hadjustment = gtk_adjustment_get_value(sheet->hadjustment);
6335 }
6336
6337 /**
6338 * gtk_sheet_set_scroll_adjustments:
6339 * @sheet: a #GtkSheet.
6340 * @hadjustment: a #GtkAdjustment
6341 * @vadjustment: a #GtkAdjustment
6342 *
6343 * Change horizontal and vertical scroll adjustments.
6344 */
6345 static void
gtk_sheet_set_scroll_adjustments(GtkSheet * sheet,GtkAdjustment * hadjustment,GtkAdjustment * vadjustment)6346 gtk_sheet_set_scroll_adjustments(GtkSheet *sheet,
6347 GtkAdjustment *hadjustment,
6348 GtkAdjustment *vadjustment)
6349 {
6350 #if GTK_SHEET_DEBUG_SIGNALS > 0
6351 g_debug("SIGNAL set-scroll-adjustments %p", sheet);
6352 #endif
6353
6354 if (sheet->hadjustment != hadjustment)
6355 gtk_sheet_set_hadjustment(sheet, hadjustment);
6356
6357 if (sheet->vadjustment != vadjustment)
6358 gtk_sheet_set_vadjustment(sheet, vadjustment);
6359 }
6360
6361 /*
6362 * gtk_sheet_finalize_handler:
6363 *
6364 * this is the #GtkSheet object class "finalize" signal handler
6365 *
6366 * @param object the #GtkSheet
6367 */
6368 static void
gtk_sheet_finalize_handler(GObject * object)6369 gtk_sheet_finalize_handler(GObject *object)
6370 {
6371 GtkSheet *sheet;
6372
6373 g_return_if_fail(object != NULL);
6374 g_return_if_fail(GTK_IS_SHEET(object));
6375
6376 sheet = GTK_SHEET(object);
6377
6378 /* get rid of all the cells */
6379 gtk_sheet_range_clear(sheet, NULL);
6380 gtk_sheet_range_delete(sheet, NULL);
6381
6382 gtk_sheet_delete_rows(sheet, 0, sheet->maxrow + 1);
6383 gtk_sheet_delete_columns(sheet, 0, sheet->maxcol + 1);
6384
6385 DeleteRow(sheet, 0, sheet->maxrow + 1);
6386 DeleteColumn(sheet, 0, sheet->maxcol + 1);
6387
6388 g_free(sheet->row);
6389 sheet->row = NULL;
6390
6391 if (sheet->column) /* free remaining column array, no gobjects there */
6392 {
6393 g_free(sheet->column);
6394 sheet->column = NULL;
6395 }
6396
6397 g_free(sheet->data);
6398 sheet->data = NULL;
6399
6400 if (sheet->title)
6401 {
6402 g_free(sheet->title);
6403 sheet->title = NULL;
6404 }
6405
6406 if (G_OBJECT_CLASS(sheet_parent_class)->finalize)
6407 (*G_OBJECT_CLASS(sheet_parent_class)->finalize)(object);
6408 }
6409
6410 /*
6411 * gtk_sheet_destroy_handler:
6412 *
6413 * this is the #GtkSheet object class "finalize" handler
6414 *
6415 * @param object
6416 */
6417 static void
gtk_sheet_destroy_handler(GtkObject * object)6418 gtk_sheet_destroy_handler(GtkObject *object)
6419 {
6420 GtkSheet *sheet;
6421 GList *children;
6422
6423 g_return_if_fail(object != NULL);
6424 g_return_if_fail(GTK_IS_SHEET(object));
6425
6426 sheet = GTK_SHEET(object);
6427
6428 /* destroy the entry */
6429 if (sheet->sheet_entry && GTK_IS_WIDGET(sheet->sheet_entry))
6430 {
6431 gtk_widget_destroy(sheet->sheet_entry);
6432 sheet->sheet_entry = NULL;
6433 }
6434
6435 /* destroy the global selection button */
6436 if (sheet->button && GTK_IS_WIDGET(sheet->button))
6437 {
6438 #if GTK_SHEET_DEBUG_REALIZE > 0
6439 g_debug("gtk_sheet_destroy: destroying old entry %p", sheet->button);
6440 #endif
6441 gtk_widget_destroy(sheet->button);
6442 sheet->button = NULL;
6443 }
6444
6445 if (sheet->timer)
6446 {
6447 g_source_remove(sheet->timer);
6448 sheet->timer = 0;
6449 }
6450
6451 if (sheet->clip_timer)
6452 {
6453 g_source_remove(sheet->clip_timer);
6454 sheet->clip_timer = 0;
6455 }
6456
6457 /* unref adjustments */
6458 if (sheet->hadjustment)
6459 {
6460 g_signal_handlers_disconnect_matched(
6461 GTK_OBJECT(sheet->hadjustment),
6462 G_SIGNAL_MATCH_DATA,
6463 0, 0, NULL, NULL, sheet);
6464 g_object_unref(G_OBJECT(sheet->hadjustment));
6465 sheet->hadjustment = NULL;
6466 }
6467 if (sheet->vadjustment)
6468 {
6469 g_signal_handlers_disconnect_matched(
6470 GTK_OBJECT(sheet->vadjustment),
6471 G_SIGNAL_MATCH_DATA,
6472 0, 0, NULL, NULL, sheet);
6473 g_object_unref(G_OBJECT(sheet->vadjustment));
6474 sheet->vadjustment = NULL;
6475 }
6476
6477 children = sheet->children;
6478 while (children)
6479 {
6480 GtkSheetChild *child = (GtkSheetChild *)children->data;
6481 if (child && child->widget)
6482 gtk_sheet_remove_handler(GTK_CONTAINER(sheet), child->widget);
6483 children = sheet->children;
6484 }
6485 sheet->children = NULL;
6486
6487 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IS_DESTROYED);
6488
6489 if (GTK_OBJECT_CLASS(sheet_parent_class)->destroy)
6490 (*GTK_OBJECT_CLASS(sheet_parent_class)->destroy)(object);
6491 }
6492
6493 /*
6494 * gtk_sheet_style_set_handler:
6495 *
6496 * this is the #GtkSheet widget class "style-set" signal handler
6497 *
6498 * @param widget the #GtkSheet
6499 * @param previous_style
6500 */
6501 static void
gtk_sheet_style_set_handler(GtkWidget * widget,GtkStyle * previous_style)6502 gtk_sheet_style_set_handler(GtkWidget *widget, GtkStyle *previous_style)
6503 {
6504 GtkSheet *sheet;
6505
6506 g_return_if_fail(widget != NULL);
6507 g_return_if_fail(GTK_IS_SHEET(widget));
6508
6509 if (GTK_WIDGET_CLASS(sheet_parent_class)->style_set)
6510 (*GTK_WIDGET_CLASS(sheet_parent_class)->style_set)(widget, previous_style);
6511
6512 sheet = GTK_SHEET(widget);
6513
6514 if (gtk_widget_get_realized(widget))
6515 {
6516 gtk_style_set_background(gtk_widget_get_style(widget),
6517 gtk_widget_get_window(widget),
6518 gtk_widget_get_state(widget));
6519 }
6520 }
6521
6522 /*
6523 * gtk_sheet_realize_handler:
6524 *
6525 * this is the #GtkSheet widget class "realize" signal handler
6526 *
6527 * @param widget the #GtkSheet
6528 */
6529 static void
gtk_sheet_realize_handler(GtkWidget * widget)6530 gtk_sheet_realize_handler(GtkWidget *widget)
6531 {
6532 GtkSheet *sheet;
6533 GdkWindowAttr attributes;
6534 gint attributes_mask;
6535 GdkGCValues values, auxvalues;
6536 GdkColormap *colormap;
6537 GtkSheetChild *child;
6538 GList *children;
6539 GtkAllocation allocation;
6540
6541 g_return_if_fail(widget != NULL);
6542 g_return_if_fail(GTK_IS_SHEET(widget));
6543
6544 sheet = GTK_SHEET(widget);
6545
6546 /* we need to recalc all positions, because row/column visibility
6547 may have changed between adding rows/cols and realisation
6548 PR#92947
6549 */
6550 _gtk_sheet_recalc_top_ypixels(sheet);
6551 _gtk_sheet_recalc_left_xpixels(sheet);
6552
6553 #if GTK_SHEET_DEBUG_REALIZE > 0
6554 g_debug("gtk_sheet_realize_handler: called (%p)", sheet->sheet_entry);
6555 #endif
6556
6557 gtk_widget_set_realized_true(GTK_WIDGET(widget));
6558
6559 gtk_widget_get_allocation(widget, &allocation);
6560 attributes.window_type = GDK_WINDOW_CHILD;
6561 attributes.x = allocation.x;
6562 attributes.y = allocation.y;
6563 attributes.width = allocation.width;
6564 attributes.height = allocation.height;
6565 attributes.wclass = GDK_INPUT_OUTPUT;
6566
6567 attributes.visual = gtk_widget_get_visual(widget);
6568 attributes.colormap = gtk_widget_get_colormap(widget);
6569
6570 attributes.event_mask = gtk_widget_get_events(widget);
6571 attributes.event_mask |= (
6572 GDK_EXPOSURE_MASK |
6573 GDK_BUTTON_PRESS_MASK |
6574 GDK_BUTTON_RELEASE_MASK |
6575 GDK_KEY_PRESS_MASK |
6576 GDK_POINTER_MOTION_MASK |
6577 GDK_POINTER_MOTION_HINT_MASK);
6578
6579 attributes_mask = GDK_WA_X | GDK_WA_Y |
6580 GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
6581
6582 attributes.cursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW);
6583
6584 /* main window */
6585 gtk_widget_set_window(widget,
6586 gdk_window_new(gtk_widget_get_parent_window(widget),
6587 &attributes, attributes_mask));
6588
6589 gdk_window_set_user_data(gtk_widget_get_window(widget), sheet);
6590
6591 gtk_widget_set_style(widget,
6592 gtk_style_attach(gtk_widget_get_style(widget),
6593 gtk_widget_get_window(widget)));
6594
6595 gtk_style_set_background(gtk_widget_get_style(widget),
6596 gtk_widget_get_window(widget),
6597 GTK_STATE_NORMAL);
6598
6599 attributes.x = 0;
6600 if (sheet->row_titles_visible)
6601 attributes.x = sheet->row_title_area.width;
6602 attributes.y = 0;
6603 attributes.width = sheet->column_title_area.width;
6604 attributes.height = sheet->column_title_area.height;
6605
6606 /* column-title window */
6607 sheet->column_title_window = gdk_window_new(
6608 gtk_widget_get_window(widget),
6609 &attributes, attributes_mask);
6610 gdk_window_set_user_data(sheet->column_title_window, sheet);
6611 gtk_style_set_background(gtk_widget_get_style(widget),
6612 sheet->column_title_window, GTK_STATE_NORMAL);
6613
6614 attributes.x = 0;
6615 attributes.y = 0;
6616 if (sheet->column_titles_visible)
6617 attributes.y = sheet->column_title_area.height;
6618 attributes.width = sheet->row_title_area.width;
6619 attributes.height = sheet->row_title_area.height;
6620
6621 /* row-title window */
6622 sheet->row_title_window = gdk_window_new(
6623 gtk_widget_get_window(widget),
6624 &attributes, attributes_mask);
6625 gdk_window_set_user_data(sheet->row_title_window, sheet);
6626 gtk_style_set_background(gtk_widget_get_style(widget),
6627 sheet->row_title_window, GTK_STATE_NORMAL);
6628
6629 /* sheet-window */
6630 attributes.cursor = gdk_cursor_new(GDK_PLUS);
6631
6632 attributes.x = 0;
6633 attributes.y = 0;
6634 attributes.width = sheet->sheet_window_width;
6635 attributes.height = sheet->sheet_window_height;
6636
6637 sheet->sheet_window = gdk_window_new(
6638 gtk_widget_get_window(widget),
6639 &attributes, attributes_mask);
6640 gdk_window_set_user_data(sheet->sheet_window, sheet);
6641
6642 gdk_window_set_background(sheet->sheet_window,
6643 &(gtk_widget_get_style(widget)->white));
6644 gdk_window_show(sheet->sheet_window);
6645
6646 /* backing_pixmap */
6647 gtk_sheet_make_backing_pixmap(sheet, 0, 0);
6648
6649 /* GCs */
6650 if (sheet->fg_gc)
6651 gdk_gc_unref(sheet->fg_gc);
6652 sheet->fg_gc = gdk_gc_new(gtk_widget_get_window(widget));
6653
6654 if (sheet->bg_gc)
6655 gdk_gc_unref(sheet->bg_gc);
6656 sheet->bg_gc = gdk_gc_new(gtk_widget_get_window(widget));
6657
6658 colormap = gtk_widget_get_colormap(widget);
6659
6660 gdk_color_white(colormap, &(gtk_widget_get_style(widget)->white));
6661 gdk_color_black(colormap, &(gtk_widget_get_style(widget)->black));
6662
6663 gdk_gc_get_values(sheet->fg_gc, &auxvalues);
6664
6665 values.foreground = gtk_widget_get_style(widget)->white;
6666 values.function = GDK_INVERT;
6667 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
6668
6669 if (sheet->xor_gc)
6670 gdk_gc_unref(sheet->xor_gc);
6671 sheet->xor_gc = gdk_gc_new_with_values(gtk_widget_get_window(widget),
6672 &values,
6673 GDK_GC_FOREGROUND |
6674 GDK_GC_FUNCTION |
6675 GDK_GC_SUBWINDOW);
6676
6677 if (gtk_widget_get_parent(sheet->sheet_entry))
6678 {
6679 g_object_ref(sheet->sheet_entry);
6680 gtk_widget_unparent(sheet->sheet_entry);
6681 }
6682 gtk_widget_set_parent_window(sheet->sheet_entry, sheet->sheet_window);
6683 gtk_widget_set_parent(sheet->sheet_entry, GTK_WIDGET(sheet));
6684
6685 if (sheet->button && gtk_widget_get_parent(sheet->button))
6686 {
6687 g_object_ref(sheet->button);
6688 gtk_widget_unparent(sheet->button);
6689 }
6690 gtk_widget_set_parent_window(sheet->button, sheet->sheet_window);
6691 gtk_widget_set_parent(sheet->button, GTK_WIDGET(sheet));
6692
6693 /*
6694 gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col);
6695 */
6696
6697 if (!sheet->cursor_drag)
6698 sheet->cursor_drag = gdk_cursor_new(GDK_PLUS);
6699
6700 if (sheet->column_titles_visible)
6701 gdk_window_show(sheet->column_title_window);
6702 if (sheet->row_titles_visible)
6703 gdk_window_show(sheet->row_title_window);
6704
6705 size_allocate_row_title_buttons(sheet);
6706 _gtk_sheet_column_buttons_size_allocate(sheet);
6707
6708 if (sheet->title) /* re-initialize title to update GUI */
6709 {
6710 gchar *existing_title = g_strdup(sheet->title);
6711 gtk_sheet_set_title(sheet, existing_title);
6712 g_free(existing_title);
6713 }
6714
6715 children = sheet->children;
6716 while (children)
6717 {
6718 child = children->data;
6719 children = children->next;
6720
6721 gtk_sheet_realize_child(sheet, child);
6722 }
6723 }
6724
6725 /*
6726 * global_button_clicked_handler:
6727 * this is the #GtkSheet global sheet button "button-press-event" handler.
6728 *
6729 * It will handle single-clicks of button 1 internally, selecting/deselecting
6730 * all sheet cells. All other button press events are propagated to the sheet.
6731 *
6732 * You cann connect your own "button-press-event" handler to the sheet
6733 * widget, and will receive all non-internally handled button-press-events.
6734 *
6735 * @param widget the global sheet button that received the signal
6736 * @param event the GdkEventButton which triggered this signal
6737 * @param data the #GtkSheet passed on signal connection
6738 *
6739 * @return TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
6740 */
6741 static gboolean
global_button_press_handler(GtkWidget * widget,GdkEventButton * event,gpointer data)6742 global_button_press_handler(GtkWidget *widget,
6743 GdkEventButton *event,
6744 gpointer data)
6745 {
6746 gboolean veto;
6747 GtkSheet *sheet = GTK_SHEET(data);
6748 gboolean retval = FALSE;
6749
6750 if (!retval
6751 && (event->type == GDK_BUTTON_PRESS)
6752 && (event->button == 1))
6753 {
6754 gtk_sheet_click_cell(sheet, -1, -1, &veto);
6755 gtk_widget_grab_focus(GTK_WIDGET(sheet));
6756 }
6757 else
6758 {
6759 g_signal_emit_by_name(GTK_WIDGET(sheet),
6760 "button_press_event",
6761 event,
6762 &retval);
6763 }
6764
6765 return(retval);
6766 }
6767
6768 static void
create_global_button(GtkSheet * sheet)6769 create_global_button(GtkSheet *sheet)
6770 {
6771 sheet->button = gtk_button_new_with_label(" ");
6772
6773 g_signal_connect(GTK_OBJECT(sheet->button),
6774 "button-press-event",
6775 G_CALLBACK(global_button_press_handler),
6776 (gpointer)sheet);
6777 }
6778
6779 static void
size_allocate_global_button(GtkSheet * sheet)6780 size_allocate_global_button(GtkSheet *sheet)
6781 {
6782 GtkAllocation allocation;
6783
6784 if (!sheet->column_titles_visible)
6785 return;
6786 if (!sheet->row_titles_visible)
6787 return;
6788
6789 gtk_widget_size_request(sheet->button, NULL);
6790
6791 allocation.x = 0;
6792 allocation.y = 0;
6793 allocation.width = sheet->row_title_area.width;
6794 allocation.height = sheet->column_title_area.height;
6795
6796 gtk_widget_size_allocate(sheet->button, &allocation);
6797 gtk_widget_show(sheet->button);
6798 }
6799
6800
6801 /*
6802 * gtk_sheet_unrealize_handler:
6803 *
6804 * this is the #GtkSheet widget class "unrealize" signal handler
6805 *
6806 * @param widget the #GtkSheet
6807 */
6808 static void
gtk_sheet_unrealize_handler(GtkWidget * widget)6809 gtk_sheet_unrealize_handler(GtkWidget *widget)
6810 {
6811 GtkSheet *sheet;
6812
6813 g_return_if_fail(widget != NULL);
6814 g_return_if_fail(GTK_IS_SHEET(widget));
6815
6816 sheet = GTK_SHEET(widget);
6817
6818 gdk_cursor_destroy(sheet->cursor_drag);
6819
6820 gdk_gc_destroy(sheet->xor_gc);
6821 gdk_gc_destroy(sheet->fg_gc);
6822 gdk_gc_destroy(sheet->bg_gc);
6823
6824 gdk_window_destroy(sheet->sheet_window);
6825 gdk_window_destroy(sheet->column_title_window);
6826 gdk_window_destroy(sheet->row_title_window);
6827
6828 if (sheet->pixmap)
6829 {
6830 g_object_unref(G_OBJECT(sheet->pixmap));
6831 sheet->pixmap = NULL;
6832 }
6833
6834 sheet->column_title_window = NULL;
6835 sheet->sheet_window = NULL;
6836 sheet->cursor_drag = NULL;
6837 sheet->xor_gc = NULL;
6838 sheet->fg_gc = NULL;
6839 sheet->bg_gc = NULL;
6840
6841 if (GTK_WIDGET_CLASS(sheet_parent_class)->unrealize)
6842 (*GTK_WIDGET_CLASS(sheet_parent_class)->unrealize)(widget);
6843 }
6844
6845 /*
6846 * gtk_sheet_map_handler:
6847 *
6848 * this is the #GtkSheet widget class "map" signal handler
6849 *
6850 * @param widget the #GtkSheet
6851 */
6852 static void
gtk_sheet_map_handler(GtkWidget * widget)6853 gtk_sheet_map_handler(GtkWidget *widget)
6854 {
6855 GtkSheet *sheet;
6856 GtkWidget *WdChild;
6857 GtkSheetChild *child;
6858 GList *children;
6859
6860 g_return_if_fail(widget != NULL);
6861 g_return_if_fail(GTK_IS_SHEET(widget));
6862
6863 sheet = GTK_SHEET(widget);
6864
6865 #if GTK_SHEET_DEBUG_EXPOSE > 0
6866 g_debug("gtk_sheet_map_handler: called");
6867 #endif
6868
6869 if (!gtk_widget_get_mapped(widget))
6870 {
6871 gtk_widget_set_mapped_true(GTK_WIDGET(widget));
6872
6873 if (!sheet->cursor_drag)
6874 sheet->cursor_drag = gdk_cursor_new(GDK_PLUS);
6875
6876 gdk_window_show(gtk_widget_get_window(widget));
6877 gdk_window_show(sheet->sheet_window);
6878
6879 if (sheet->column_titles_visible)
6880 {
6881 _gtk_sheet_column_buttons_size_allocate(sheet);
6882 gdk_window_show(sheet->column_title_window);
6883 }
6884
6885 if (sheet->row_titles_visible)
6886 {
6887 size_allocate_row_title_buttons(sheet);
6888 gdk_window_show(sheet->row_title_window);
6889 }
6890
6891 #if 0
6892 /* this will be done by gtk_sheet_activate_cell() below,
6893 it causes trouble when there is no active cell in the sheet,
6894 because sheet_entry will start to process events */
6895 if (!gtk_widget_get_mapped (sheet->sheet_entry))
6896 {
6897 gtk_widget_show (sheet->sheet_entry);
6898 gtk_widget_map (sheet->sheet_entry);
6899 }
6900 #endif
6901
6902 if (gtk_widget_get_visible(sheet->button) &&
6903 !gtk_widget_get_mapped(sheet->button))
6904 {
6905 gtk_widget_show(sheet->button);
6906 gtk_widget_map(sheet->button);
6907 }
6908
6909 if ((WdChild = gtk_bin_get_child(GTK_BIN(sheet->button))))
6910 {
6911 if (gtk_widget_get_visible(WdChild) &&
6912 !gtk_widget_get_mapped(WdChild))
6913 {
6914 gtk_widget_map(WdChild);
6915 }
6916 }
6917
6918 #if GTK_SHEET_DEBUG_EXPOSE > 0
6919 g_debug("gtk_sheet_map_handler: calling _gtk_sheet_range_draw");
6920 #endif
6921
6922 _gtk_sheet_recalc_view_range(sheet);
6923 _gtk_sheet_range_draw(sheet, NULL, TRUE);
6924
6925 /* this was already done above - why again?
6926 gtk_sheet_activate_cell(sheet,
6927 sheet->active_cell.row,
6928 sheet->active_cell.col);
6929 */
6930
6931 children = sheet->children;
6932 while (children)
6933 {
6934 child = children->data;
6935 children = children->next;
6936
6937 if (gtk_widget_get_visible(child->widget) &&
6938 !gtk_widget_get_mapped(child->widget))
6939 {
6940 gtk_widget_map(child->widget);
6941 gtk_sheet_position_child(sheet, child);
6942 }
6943 }
6944 }
6945 }
6946
6947 /*
6948 * gtk_sheet_unmap_handler:
6949 *
6950 * this is the #GtkSheet widget class "unmap" signal handler
6951 *
6952 * @param widget the #GtkSheet
6953 */
6954 static void
gtk_sheet_unmap_handler(GtkWidget * widget)6955 gtk_sheet_unmap_handler(GtkWidget *widget)
6956 {
6957 GtkSheet *sheet;
6958 GtkSheetChild *child;
6959 GList *children;
6960
6961 g_return_if_fail(widget != NULL);
6962 g_return_if_fail(GTK_IS_SHEET(widget));
6963
6964 sheet = GTK_SHEET(widget);
6965
6966 #if GTK_SHEET_DEBUG_DRAW > 0
6967 g_debug("gtk_sheet_unmap: called");
6968 #endif
6969
6970 if (gtk_widget_get_mapped(widget))
6971 {
6972 gtk_widget_set_mapped_false(GTK_WIDGET(widget));
6973
6974 gdk_window_hide(sheet->sheet_window);
6975
6976 if (sheet->column_titles_visible)
6977 gdk_window_hide(sheet->column_title_window);
6978
6979 if (sheet->row_titles_visible)
6980 gdk_window_hide(sheet->row_title_window);
6981
6982 gdk_window_hide(gtk_widget_get_window(widget));
6983
6984 if (gtk_widget_get_mapped(sheet->sheet_entry))
6985 gtk_widget_unmap(sheet->sheet_entry);
6986
6987 if (gtk_widget_get_mapped(sheet->button))
6988 gtk_widget_unmap(sheet->button);
6989
6990 children = sheet->children;
6991 while (children)
6992 {
6993 child = children->data;
6994 children = children->next;
6995
6996 if (gtk_widget_get_visible(child->widget) &&
6997 gtk_widget_get_mapped(child->widget))
6998 {
6999 gtk_widget_unmap(child->widget);
7000 }
7001 }
7002
7003 }
7004 }
7005
7006 /*
7007 * gtk_sheet_draw_tm - draw tooltip marker
7008 *
7009 * @param sheet
7010 */
7011 static void
gtk_sheet_draw_tooltip_marker(GtkSheet * sheet,GtkSheetArea area,const gint row,const gint col)7012 gtk_sheet_draw_tooltip_marker(GtkSheet *sheet,
7013 GtkSheetArea area,
7014 const gint row, const gint col)
7015 {
7016 switch(area)
7017 {
7018 case ON_CELL_AREA:
7019 if (row <= sheet->maxallocrow && col <= sheet->maxalloccol
7020 && sheet->data[row] && sheet->data[row][col])
7021 {
7022 GtkSheetCell *cell = sheet->data[row][col];
7023
7024 if (cell->tooltip_markup || cell->tooltip_text)
7025 {
7026 GdkPoint p[3];
7027
7028 gdk_gc_set_foreground(sheet->bg_gc, &sheet->tm_color);
7029
7030 p[0].x = _gtk_sheet_column_left_xpixel(sheet, col) + COLPTR(sheet, col)->width
7031 - GTK_SHEET_DEFAULT_TM_SIZE;
7032 p[0].y = _gtk_sheet_row_top_ypixel(sheet, row) + 1;
7033
7034 p[1].x = p[0].x + GTK_SHEET_DEFAULT_TM_SIZE;
7035 p[1].y = p[0].y;
7036
7037 p[2].x = p[1].x;
7038 p[2].y = p[1].y + GTK_SHEET_DEFAULT_TM_SIZE;
7039
7040 /* draw cell tooltip marker */
7041 gdk_draw_polygon(sheet->pixmap,
7042 sheet->bg_gc,
7043 TRUE, p, 3);
7044 }
7045 }
7046 break;
7047
7048 case ON_ROW_TITLES_AREA:
7049 if (0 <= row && row <= sheet->maxrow)
7050 {
7051 GtkSheetRow *rowp = ROWPTR(sheet, row);
7052 GdkWindow *window = sheet->row_title_window;
7053
7054 if (rowp->tooltip_markup || rowp->tooltip_text)
7055 {
7056 GdkPoint p[3];
7057
7058 gdk_gc_set_foreground(sheet->bg_gc, &sheet->tm_color);
7059
7060 p[0].x = sheet->row_title_area.width - 1
7061 - GTK_SHEET_DEFAULT_TM_SIZE;
7062 p[0].y = _gtk_sheet_row_top_ypixel(sheet, row) + 1;
7063 if (sheet->column_titles_visible)
7064 p[0].y -= sheet->column_title_area.height;
7065
7066 p[1].x = p[0].x + GTK_SHEET_DEFAULT_TM_SIZE;
7067 p[1].y = p[0].y;
7068
7069 p[2].x = p[1].x;
7070 p[2].y = p[1].y + GTK_SHEET_DEFAULT_TM_SIZE;
7071
7072 /* draw cell tooltip marker */
7073 gdk_draw_polygon(window,
7074 sheet->bg_gc,
7075 TRUE, p, 3);
7076 }
7077 }
7078 break;
7079
7080 case ON_COLUMN_TITLES_AREA:
7081 if (0 <= col && col <= sheet->maxcol)
7082 {
7083 GtkSheetColumn *column = COLPTR(sheet, col);
7084 GdkWindow *window = sheet->column_title_window;
7085
7086 if (gtk_widget_get_has_tooltip(GTK_WIDGET(column)))
7087 {
7088 GdkPoint p[3];
7089
7090 gdk_gc_set_foreground(sheet->bg_gc, &sheet->tm_color);
7091
7092 p[0].x = _gtk_sheet_column_right_xpixel(sheet, col) + CELL_SPACING - 1
7093 - GTK_SHEET_DEFAULT_TM_SIZE;
7094 if (sheet->row_titles_visible)
7095 p[0].x -= sheet->row_title_area.width;
7096 p[0].y = 0;
7097
7098 p[1].x = p[0].x + GTK_SHEET_DEFAULT_TM_SIZE;
7099 p[1].y = p[0].y;
7100
7101 p[2].x = p[1].x;
7102 p[2].y = p[1].y + GTK_SHEET_DEFAULT_TM_SIZE;
7103
7104 /* draw cell tooltip marker */
7105 gdk_draw_polygon(window,
7106 sheet->bg_gc,
7107 TRUE, p, 3);
7108 }
7109 }
7110 break;
7111
7112 default:
7113 return;
7114 }
7115 }
7116
7117 static void
_cell_draw_background(GtkSheet * sheet,gint row,gint col)7118 _cell_draw_background(GtkSheet *sheet, gint row, gint col)
7119 {
7120 GtkWidget *widget;
7121 GdkGC * fg_gc, *bg_gc;
7122 GdkRectangle area;
7123
7124 g_return_if_fail(sheet != NULL);
7125
7126 /* bail now if we arn't drawable yet */
7127 if (!gtk_widget_is_drawable(GTK_WIDGET(sheet)))
7128 return;
7129
7130 if (row < 0 || row > sheet->maxrow)
7131 return;
7132 if (col < 0 || col > sheet->maxcol)
7133 return;
7134 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)))
7135 return;
7136 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)))
7137 return;
7138
7139 widget = GTK_WIDGET(sheet);
7140
7141 GtkSheetCellAttr attributes;
7142 gtk_sheet_get_attributes(sheet, row, col, &attributes);
7143
7144 /* select GC for background rectangle */
7145 gdk_gc_set_foreground(sheet->fg_gc, &attributes.foreground);
7146 gdk_gc_set_foreground(sheet->bg_gc, &attributes.background);
7147
7148 fg_gc = sheet->fg_gc;
7149 bg_gc = sheet->bg_gc;
7150
7151 area.x = _gtk_sheet_column_left_xpixel(sheet, col);
7152 area.y = _gtk_sheet_row_top_ypixel(sheet, row);
7153 area.width = COLPTR(sheet, col)->width;
7154 area.height = ROWPTR(sheet, row)->height;
7155
7156 #if GTK_SHEET_DEBUG_DRAW_BACKGROUND>0
7157 #if 1
7158 g_debug("_cell_draw_background(%d,%d): cellbg x %d y %d w %d h %d %s",
7159 row, col,
7160 area.x, area.y, area.width, area.height,
7161 gdk_color_to_string(&attributes.background));
7162 #endif
7163 #endif
7164
7165 /* fill cell background */
7166 gdk_draw_rectangle(sheet->pixmap,
7167 bg_gc,
7168 TRUE,
7169 area.x, area.y,
7170 area.width, area.height);
7171
7172 gdk_gc_set_line_attributes(sheet->fg_gc, 1, 0, 0, 0);
7173
7174 if (sheet->show_grid)
7175 {
7176 gdk_gc_set_foreground(sheet->bg_gc, &sheet->grid_color);
7177
7178 #if GTK_SHEET_DEBUG_DRAW_BACKGROUND>0
7179 #if 0
7180 g_debug("_cell_draw_background(%d,%d): grid x %d y %d w %d h %d %s",
7181 row, col,
7182 area.x, area.y, area.width, area.height,
7183 gdk_color_to_string(&attributes.background));
7184 #endif
7185 #endif
7186
7187 /* draw grid rectangle */
7188 gdk_draw_rectangle(sheet->pixmap,
7189 sheet->bg_gc,
7190 FALSE,
7191 area.x, area.y,
7192 area.width, area.height);
7193 }
7194
7195 gtk_sheet_draw_tooltip_marker(sheet, ON_CELL_AREA, row, col);
7196 }
7197
7198 static void
_cell_draw_border(GtkSheet * sheet,gint row,gint col,gint mask)7199 _cell_draw_border(GtkSheet *sheet, gint row, gint col, gint mask)
7200 {
7201 GtkWidget *widget;
7202 GdkGC * fg_gc, *bg_gc;
7203 GdkRectangle area;
7204 guint width;
7205
7206 g_return_if_fail(sheet != NULL);
7207
7208 /* bail now if we arn't drawable yet */
7209 if (!gtk_widget_is_drawable(GTK_WIDGET(sheet)))
7210 return;
7211
7212 if (row < 0 || row > sheet->maxrow)
7213 return;
7214 if (col < 0 || col > sheet->maxcol)
7215 return;
7216 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)))
7217 return;
7218 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)))
7219 return;
7220
7221 widget = GTK_WIDGET(sheet);
7222
7223 GtkSheetCellAttr attributes;
7224 gtk_sheet_get_attributes(sheet, row, col, &attributes);
7225
7226 /* select GC for background rectangle */
7227 gdk_gc_set_foreground(sheet->fg_gc, &attributes.border.color);
7228 gdk_gc_set_foreground(sheet->bg_gc, &attributes.background);
7229
7230 fg_gc = sheet->fg_gc;
7231 bg_gc = sheet->bg_gc;
7232
7233 area.x = _gtk_sheet_column_left_xpixel(sheet, col);
7234 area.y = _gtk_sheet_row_top_ypixel(sheet, row);
7235 area.width = COLPTR(sheet, col)->width;
7236 area.height = sheet->row[row].height;
7237
7238 width = attributes.border.width;
7239 gdk_gc_set_line_attributes(sheet->fg_gc, attributes.border.width,
7240 attributes.border.line_style,
7241 attributes.border.cap_style,
7242 attributes.border.join_style);
7243 if (width > 0)
7244 {
7245
7246 if (attributes.border.mask & GTK_SHEET_LEFT_BORDER & mask)
7247 gdk_draw_line(sheet->pixmap, sheet->fg_gc,
7248 area.x, area.y - width / 2,
7249 area.x, area.y + area.height + width / 2 + 1);
7250
7251 if (attributes.border.mask & GTK_SHEET_RIGHT_BORDER & mask)
7252 gdk_draw_line(sheet->pixmap, sheet->fg_gc,
7253 area.x + area.width, area.y - width / 2,
7254 area.x + area.width,
7255 area.y + area.height + width / 2 + 1);
7256
7257 if (attributes.border.mask & GTK_SHEET_TOP_BORDER & mask)
7258 gdk_draw_line(sheet->pixmap, sheet->fg_gc,
7259 area.x - width / 2, area.y,
7260 area.x + area.width + width / 2 + 1,
7261 area.y);
7262
7263 if (attributes.border.mask & GTK_SHEET_BOTTOM_BORDER & mask)
7264 gdk_draw_line(sheet->pixmap, sheet->fg_gc,
7265 area.x - width / 2, area.y + area.height,
7266 area.x + area.width + width / 2 + 1,
7267 area.y + area.height);
7268 }
7269
7270 }
7271
7272
7273 static void
_cell_draw_label(GtkSheet * sheet,gint row,gint col)7274 _cell_draw_label(GtkSheet *sheet, gint row, gint col)
7275 {
7276 GtkWidget *widget;
7277 GdkRectangle area, clip_area;
7278 gint i;
7279 gint text_width, text_height, y;
7280 gint xoffset = 0;
7281 gint size, sizel, sizer;
7282 GdkGC *gc;
7283 PangoLayout *layout;
7284 PangoRectangle rect;
7285 PangoFontMetrics *metrics;
7286 PangoContext *context = gtk_widget_get_pango_context(GTK_WIDGET(sheet));
7287 gint ascent, descent, spacing, y_pos;
7288 GtkSheetVerticalJustification vjust;
7289
7290 gchar * label, *dataformat;
7291
7292 g_return_if_fail(sheet != NULL);
7293
7294 /* bail now if we aren't drawable yet */
7295 if (!GTK_WIDGET_DRAWABLE(sheet))
7296 return;
7297
7298 if (row < 0 || row > sheet->maxallocrow)
7299 return;
7300 if (col < 0 || col > sheet->maxalloccol)
7301 return;
7302
7303 if (!sheet->data[row])
7304 return;
7305 if (!sheet->data[row][col])
7306 return;
7307 if (!sheet->data[row][col]->text || !sheet->data[row][col]->text[0])
7308 return;
7309
7310 if (row < 0 || row > sheet->maxrow)
7311 return;
7312 if (col < 0 || col > sheet->maxcol)
7313 return;
7314
7315 /* bail now if we aren't drawable yet */
7316 if (!gtk_widget_is_drawable(GTK_WIDGET(sheet)))
7317 return;
7318
7319 GtkSheetColumn *colptr = COLPTR(sheet, col);
7320
7321 if (!GTK_SHEET_COLUMN_IS_VISIBLE(colptr))
7322 return;
7323 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)))
7324 return;
7325
7326 widget = GTK_WIDGET(sheet);
7327
7328 label = sheet->data[row][col]->text;
7329 dataformat = gtk_sheet_column_get_format(sheet, col);
7330
7331 if (dataformat)
7332 label = gtk_data_format(label, dataformat);
7333
7334 GtkSheetCellAttr attributes;
7335 gtk_sheet_get_attributes(sheet, row, col, &attributes);
7336
7337 /* select GC for background rectangle */
7338 gdk_gc_set_foreground(sheet->fg_gc, &attributes.foreground);
7339 gdk_gc_set_background(sheet->fg_gc, &attributes.background);
7340
7341 gc = sheet->fg_gc;
7342
7343 area.x = _gtk_sheet_column_left_xpixel(sheet, col);
7344 area.y = _gtk_sheet_row_top_ypixel(sheet, row);
7345 area.width = colptr->width;
7346 area.height = ROWPTR(sheet, row)->height;
7347
7348 clip_area = area;
7349
7350 layout = gtk_widget_create_pango_layout(GTK_WIDGET(sheet), label);
7351 pango_layout_set_font_description(layout, attributes.font_desc);
7352
7353 if (!gtk_sheet_autoresize_columns(sheet))
7354 {
7355 switch(colptr->wrap_mode)
7356 {
7357 case GTK_WRAP_NONE:
7358 break;
7359
7360 case GTK_WRAP_CHAR:
7361 pango_layout_set_width(layout, colptr->width * PANGO_SCALE);
7362 pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
7363 break;
7364
7365 case GTK_WRAP_WORD:
7366 pango_layout_set_width(layout, colptr->width * PANGO_SCALE);
7367 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
7368 break;
7369
7370 case GTK_WRAP_WORD_CHAR:
7371 pango_layout_set_width(layout, colptr->width * PANGO_SCALE);
7372 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
7373 break;
7374 }
7375 }
7376
7377 pango_layout_get_pixel_extents(layout, NULL, &rect);
7378
7379 metrics = pango_context_get_metrics(context,
7380 attributes.font_desc, pango_context_get_language(context));
7381
7382 ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
7383 descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
7384 spacing = pango_layout_get_spacing(layout) / PANGO_SCALE;
7385
7386 pango_font_metrics_unref(metrics);
7387
7388 /* Align primarily for locale's ascent/descent */
7389
7390 /* vertical cell text justification */
7391 {
7392 /* column->vjust overrides sheet->vjust */
7393
7394 vjust = colptr->vjust;
7395 if (vjust == GTK_SHEET_VERTICAL_JUSTIFICATION_DEFAULT)
7396 vjust = sheet->vjust;
7397
7398 /* Vertical justification is quantisized, so that all text lines using the
7399 same font appear vertically aligned, even if adjacent columns have
7400 different vjust settings.
7401 */
7402 switch(vjust)
7403 {
7404 case GTK_SHEET_VERTICAL_JUSTIFICATION_DEFAULT:
7405 case GTK_SHEET_VERTICAL_JUSTIFICATION_TOP:
7406 y_pos = CELLOFFSET;
7407 break;
7408
7409 case GTK_SHEET_VERTICAL_JUSTIFICATION_MIDDLE:
7410 {
7411 /* the following works only if the whole text is set with same metrics */
7412 register gint line_height = ascent + descent + spacing;
7413 register gint area_lines = area.height / line_height;
7414 register gint text_lines = rect.height / line_height;
7415
7416 y_pos = CELLOFFSET - ((text_lines - area_lines) / 2) * line_height;
7417 }
7418 break;
7419
7420 case GTK_SHEET_VERTICAL_JUSTIFICATION_BOTTOM:
7421 {
7422 /* the following works only if the whole text is set with same metrics */
7423 register gint line_height = ascent + descent + spacing;
7424 register gint area_lines = area.height / line_height;
7425 register gint area_height_quant = area_lines * line_height;
7426
7427 y_pos = CELLOFFSET + area_height_quant - rect.height;
7428 }
7429 break;
7430 }
7431
7432 y = area.y + y_pos;
7433 }
7434
7435 text_width = rect.width;
7436 text_height = rect.height;
7437
7438
7439 switch(attributes.justification)
7440 {
7441 case GTK_JUSTIFY_RIGHT:
7442 size = area.width; /* start with col size */
7443 area.x += area.width; /* anchor clip_area at right */
7444
7445 if (!gtk_sheet_clip_text(sheet)) /* text extends multiple cells */
7446 {
7447 for (i = col - 1; i >= MIN_VIEW_COLUMN(sheet); i--)
7448 {
7449 GtkSheetColumn *cpi = COLPTR(sheet, i);
7450
7451 if (i < 0 || i > sheet->maxcol)
7452 break;
7453
7454 if (!GTK_SHEET_COLUMN_IS_VISIBLE(cpi))
7455 continue;
7456 if (gtk_sheet_cell_get_text(sheet, row, i))
7457 break;
7458 if (size >= text_width + CELLOFFSET)
7459 break;
7460
7461 size += cpi->width; /* extend to left */
7462
7463 #if GTK_SHEET_OPTIMIZE_COLUMN_DRAW>0
7464 /* note: this column draws text on cpi */
7465 cpi->right_text_column = MAX(col, cpi->right_text_column);
7466 #if GTK_SHEET_DEBUG_DRAW > 0
7467 g_debug("_cell_draw_label: right_text_column %d = %d",
7468 i, cpi->right_text_column);
7469 #endif
7470 #endif
7471 }
7472 area.width = size; /* extend clip area */
7473 }
7474 area.x -= size; /* shift left */
7475 xoffset += area.width - text_width - 2 * CELLOFFSET - attributes.border.width / 2;
7476 break;
7477
7478 case GTK_JUSTIFY_CENTER:
7479 sizel = sizer = area.width / 2; /* start with half col size*/
7480 area.x += area.width / 2; /* anchor clip_area at center */
7481
7482 if (!gtk_sheet_clip_text(sheet)) /* text extends multiple cells */
7483 {
7484 for (i = col + 1; i <= MAX_VIEW_COLUMN(sheet) && i <= sheet->maxcol; i++)
7485 {
7486 GtkSheetColumn *cpi = COLPTR(sheet, i);
7487
7488 if (!GTK_SHEET_COLUMN_IS_VISIBLE(cpi))
7489 continue;
7490 if (gtk_sheet_cell_get_text(sheet, row, i))
7491 break;
7492 if (sizer >= text_width / 2)
7493 break;
7494
7495 sizer += cpi->width; /* extend to right */
7496
7497 #if GTK_SHEET_OPTIMIZE_COLUMN_DRAW>0
7498 /* note: this column draws text on cpi */
7499 cpi->left_text_column = MIN(col, cpi->left_text_column);
7500 #if GTK_SHEET_DEBUG_DRAW > 0
7501 g_debug("_cell_draw_label: left_text_column %d = %d",
7502 i, cpi->left_text_column);
7503 #endif
7504 #endif
7505 }
7506 for (i = col - 1; i >= MIN_VIEW_COLUMN(sheet); i--)
7507 {
7508 GtkSheetColumn *cpi = COLPTR(sheet, i);
7509
7510 if (i < 0 || i > sheet->maxcol)
7511 break;
7512
7513 if (!GTK_SHEET_COLUMN_IS_VISIBLE(cpi))
7514 continue;
7515 if (gtk_sheet_cell_get_text(sheet, row, i))
7516 break;
7517 if (sizel >= text_width / 2)
7518 break;
7519
7520 sizel += cpi->width; /* extend to left */
7521
7522 #if GTK_SHEET_OPTIMIZE_COLUMN_DRAW>0
7523 /* note: this column draws text on cpi */
7524 cpi->right_text_column = MAX(col, cpi->right_text_column);
7525 #if GTK_SHEET_DEBUG_DRAW > 0
7526 g_debug("_cell_draw_label: right_text_column %d = %d",
7527 i, cpi->right_text_column);
7528 #endif
7529 #endif
7530 }
7531 area.width = sizel + sizer; /* extend clip area */
7532 }
7533 area.x -= sizel; /* shift left */
7534 xoffset += sizel - text_width / 2 - CELLOFFSET;
7535 break;
7536
7537 case GTK_JUSTIFY_LEFT:
7538 default:
7539 size = area.width; /* start with col size, anchor at left */
7540
7541 if (!gtk_sheet_clip_text(sheet)) /* text extends multiple cells */
7542 {
7543 for (i = col + 1; i <= MAX_VIEW_COLUMN(sheet) && i <= sheet->maxcol; i++)
7544 {
7545 GtkSheetColumn *cpi = COLPTR(sheet, i);
7546
7547 if (!GTK_SHEET_COLUMN_IS_VISIBLE(cpi))
7548 continue;
7549 if (gtk_sheet_cell_get_text(sheet, row, i))
7550 break;
7551 if (size >= text_width + CELLOFFSET)
7552 break;
7553
7554 size += cpi->width; /* extend to right */
7555
7556 #if GTK_SHEET_OPTIMIZE_COLUMN_DRAW>0
7557 /* note: this column draws text on cpi */
7558 cpi->left_text_column = MIN(col, cpi->left_text_column);
7559 #if GTK_SHEET_DEBUG_DRAW > 0
7560 g_debug("_cell_draw_label: left_text_column %d = %d",
7561 i, cpi->left_text_column);
7562 #endif
7563 #endif
7564 }
7565 area.width = size; /* extend clip area */
7566 }
7567 xoffset += attributes.border.width / 2;
7568 break;
7569 }
7570
7571 if (!gtk_sheet_clip_text(sheet)) /* text extends multiple cells */
7572 clip_area = area;
7573 gdk_gc_set_clip_rectangle(gc, &clip_area);
7574
7575 #if GTK_SHEET_DEBUG_DRAW_LABEL>0
7576 #if 0
7577 g_debug("_cell_draw_label(%d,%d): clip x %d y %d w %d h %d",
7578 row, col,
7579 clip_area.x, clip_area.y, clip_area.width, clip_area.height);
7580 #endif
7581 #endif
7582
7583
7584 #if GTK_SHEET_DEBUG_DRAW_LABEL>0
7585 #if 1
7586 g_debug("_cell_draw_label(%d,%d): x %d y %d fg %s bg %s",
7587 row, col,
7588 area.x + xoffset + CELLOFFSET, y,
7589 gdk_color_to_string(&attributes.foreground),
7590 gdk_color_to_string(&attributes.background)
7591 );
7592 #endif
7593 #endif
7594
7595 gdk_draw_layout(sheet->pixmap, gc,
7596 area.x + xoffset + CELLOFFSET, y,
7597 layout);
7598
7599 g_object_unref(G_OBJECT(layout));
7600
7601 /* copy sheet->pixmap to window */
7602
7603 gdk_draw_pixmap(sheet->sheet_window,
7604 gc,
7605 sheet->pixmap,
7606 area.x, area.y,
7607 area.x, area.y,
7608 area.width, area.height);
7609
7610 gdk_gc_set_clip_rectangle(gc, NULL);
7611 }
7612
7613
7614
7615 /**
7616 * _gtk_sheet_range_draw:
7617 * @sheet: the #GtkSheet
7618 * @range: the #GtkSheetRange or NULL
7619 * @activate_active_cell: TRUE to activate active cell after
7620 * drawing
7621 *
7622 * draw visible part of range.
7623 * If @range == NULL then draw the whole screen.
7624 *
7625 */
7626 void
_gtk_sheet_range_draw(GtkSheet * sheet,const GtkSheetRange * range,gboolean activate_active_cell)7627 _gtk_sheet_range_draw(GtkSheet *sheet,
7628 const GtkSheetRange *range,
7629 gboolean activate_active_cell)
7630 {
7631 gint row, col;
7632 GtkSheetRange drawing_range;
7633 GdkRectangle area;
7634
7635 g_return_if_fail(sheet != NULL);
7636 g_return_if_fail(GTK_SHEET(sheet));
7637
7638 #if GTK_SHEET_DEBUG_DRAW > 0
7639 g_debug("_gtk_sheet_range_draw: called");
7640 #endif
7641
7642 if (!gtk_widget_is_drawable(GTK_WIDGET(sheet)))
7643 return;
7644 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
7645 return;
7646 if (!gtk_widget_get_mapped(GTK_WIDGET(sheet)))
7647 return;
7648
7649 if (range)
7650 {
7651 drawing_range.row0 = MAX(range->row0, MIN_VIEW_ROW(sheet));
7652 drawing_range.rowi = MIN(range->rowi, MAX_VIEW_ROW(sheet));
7653 drawing_range.col0 = MAX(range->col0, MIN_VIEW_COLUMN(sheet));
7654 drawing_range.coli = MIN(range->coli, MAX_VIEW_COLUMN(sheet));
7655 }
7656 else
7657 {
7658 drawing_range.row0 = MIN_VIEW_ROW(sheet);
7659 drawing_range.rowi = MAX_VIEW_ROW(sheet);
7660 drawing_range.col0 = MIN_VIEW_COLUMN(sheet);
7661 drawing_range.coli = MAX_VIEW_COLUMN(sheet);
7662 }
7663
7664 #if GTK_SHEET_DEBUG_DRAW > 0
7665 g_debug("_gtk_sheet_range_draw: row %d - %d col %d - %d",
7666 drawing_range.row0, drawing_range.rowi, drawing_range.col0, drawing_range.coli);
7667 #endif
7668
7669 if (drawing_range.row0 > drawing_range.rowi)
7670 return;
7671 if (drawing_range.col0 > drawing_range.coli)
7672 return;
7673
7674 /*
7675 gdk_draw_rectangle (sheet->pixmap,
7676 GTK_WIDGET(sheet)->style->white_gc,
7677 TRUE,
7678 0,0,
7679 sheet->sheet_window_width,sheet->sheet_window_height);
7680 */
7681
7682 /* clear outer area beyond rightmost column */
7683 if (drawing_range.coli >= MAX_VIEW_COLUMN(sheet))
7684 {
7685 gint maxcol = MAX_VIEW_COLUMN(sheet); /* might not be visible */
7686
7687 if (maxcol > sheet->maxcol)
7688 maxcol = sheet->maxcol;
7689
7690 while (maxcol >= 0
7691 && !GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, maxcol))) --maxcol;
7692
7693 if (maxcol >= 0)
7694 {
7695 area.x = _gtk_sheet_column_left_xpixel(sheet, maxcol) +
7696 COLPTR(sheet, maxcol)->width;
7697 }
7698 else
7699 {
7700 area.x = sheet->hoffset;
7701 if (sheet->row_titles_visible)
7702 area.x += sheet->row_title_area.width;
7703 }
7704 area.width = sheet->sheet_window_width - area.x;
7705 area.y = 0;
7706 area.height = sheet->sheet_window_height;
7707
7708 if (area.width > 0) /* beware, rightmost column might be partially visible */
7709 {
7710 #if 0
7711 gdk_gc_set_foreground(sheet->fg_gc, &sheet->bg_color);
7712 #else
7713 gdk_gc_set_foreground(sheet->fg_gc,
7714 >k_widget_get_style(GTK_WIDGET(sheet))->bg[GTK_STATE_NORMAL]);
7715 #endif
7716
7717 gdk_draw_rectangle(sheet->pixmap,
7718 sheet->fg_gc,
7719 TRUE, /* filled */
7720 area.x, area.y,
7721 area.width, area.height);
7722
7723 gdk_draw_pixmap(sheet->sheet_window,
7724 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
7725 sheet->pixmap,
7726 area.x, area.y,
7727 area.x, area.y,
7728 area.width, area.height);
7729 }
7730 }
7731
7732 /* clear outer area beyond last row */
7733 if (drawing_range.rowi >= MAX_VIEW_ROW(sheet))
7734 {
7735 gint maxrow = MAX_VIEW_ROW(sheet); /* might not be visible */
7736
7737 if (maxrow > sheet->maxrow)
7738 maxrow = sheet->maxrow;
7739
7740 while (maxrow >= 0
7741 && !GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, maxrow))) --maxrow;
7742
7743 area.x = 0;
7744 area.width = sheet->sheet_window_width;
7745
7746 if (maxrow >= 0)
7747 {
7748 area.y = _gtk_sheet_row_top_ypixel(sheet, maxrow) +
7749 ROWPTR(sheet, maxrow)->height;
7750 }
7751 else
7752 {
7753 area.y = sheet->voffset;
7754 if (sheet->column_titles_visible)
7755 area.y += sheet->column_title_area.height;
7756 }
7757 area.height = sheet->sheet_window_height - area.y;
7758
7759 if (area.height > 0) /* beware, last row might be partially visible */
7760 {
7761 #if 0
7762 gdk_gc_set_foreground(sheet->fg_gc, &sheet->bg_color);
7763 #else
7764 gdk_gc_set_foreground(sheet->fg_gc,
7765 >k_widget_get_style(GTK_WIDGET(sheet))->bg[GTK_STATE_NORMAL]);
7766 #endif
7767
7768 gdk_draw_rectangle(sheet->pixmap,
7769 sheet->fg_gc,
7770 TRUE, /* filled */
7771 area.x, area.y,
7772 area.width, area.height);
7773
7774 gdk_draw_pixmap(sheet->sheet_window,
7775 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
7776 sheet->pixmap,
7777 area.x, area.y,
7778 area.x, area.y,
7779 area.width, area.height);
7780 }
7781 }
7782
7783 /* extend the drawing range to include all text sources */
7784
7785 if (drawing_range.col0 < 0)
7786 drawing_range.col0 = 0;
7787 if (drawing_range.col0 > sheet->maxcol)
7788 drawing_range.col0 = sheet->maxcol;
7789 if (drawing_range.coli < 0)
7790 drawing_range.coli = 0;
7791 if (drawing_range.coli > sheet->maxcol)
7792 drawing_range.coli = sheet->maxcol;
7793
7794 if (!gtk_sheet_clip_text(sheet)) /* text extends multiple cells */
7795 {
7796 drawing_range.col0 = MIN_VIEW_COLUMN(sheet);
7797 drawing_range.coli = MAX_VIEW_COLUMN(sheet);
7798
7799 #if GTK_SHEET_DEBUG_DRAW > 0
7800 g_debug("_gtk_sheet_range_draw: extended: row %d - %d col %d - %d",
7801 drawing_range.row0, drawing_range.rowi, drawing_range.col0, drawing_range.coli);
7802 #endif
7803 }
7804
7805 /* draw grid and cells */
7806 for (row = drawing_range.row0; row <= drawing_range.rowi; row++)
7807 {
7808 for (col = drawing_range.col0; col <= drawing_range.coli; col++)
7809 {
7810 _cell_draw_background(sheet, row, col);
7811 }
7812 }
7813
7814 for (row = drawing_range.row0; row <= drawing_range.rowi; row++)
7815 {
7816 for (col = drawing_range.col0; col <= drawing_range.coli; col++)
7817 {
7818 _cell_draw_border(sheet, row - 1, col, GTK_SHEET_BOTTOM_BORDER);
7819 _cell_draw_border(sheet, row + 1, col, GTK_SHEET_TOP_BORDER);
7820 _cell_draw_border(sheet, row, col - 1, GTK_SHEET_RIGHT_BORDER);
7821 _cell_draw_border(sheet, row, col + 1, GTK_SHEET_LEFT_BORDER);
7822 _cell_draw_border(sheet, row, col, 15);
7823 }
7824 }
7825
7826 /* draw text within range (1) */
7827
7828 #if GTK_SHEET_DEBUG_DRAW > 0
7829 g_debug("_gtk_sheet_range_draw: (1) row %d - %d col %d - %d",
7830 drawing_range.row0, drawing_range.rowi,
7831 drawing_range.col0, drawing_range.coli);
7832 #endif
7833
7834 for (row = drawing_range.row0; row <= drawing_range.rowi; row++)
7835 {
7836 for (col = drawing_range.col0; col <= drawing_range.coli; col++)
7837 {
7838 if (row <= sheet->maxallocrow && col <= sheet->maxalloccol &&
7839 sheet->data[row] && sheet->data[row][col])
7840 {
7841 _cell_draw_label(sheet, row, col);
7842 }
7843 }
7844 }
7845 #if 0
7846 /* draw text left outside range (2) */
7847
7848 if (0 <= drawing_range.col0 && drawing_range.col0 <= sheet->maxcol)
7849 {
7850 #if GTK_SHEET_DEBUG_DRAW > 0
7851 g_debug("_gtk_sheet_range_draw: (2) row %d - %d col %d - %d",
7852 drawing_range.row0, drawing_range.rowi,
7853 COLPTR(sheet, drawing_range.col0)->left_text_column, drawing_range.col0 - 1);
7854 #endif
7855
7856 for (row = drawing_range.row0; row <= drawing_range.rowi; row++)
7857 {
7858 for (col = COLPTR(sheet, drawing_range.col0)->left_text_column;
7859 col < drawing_range.col0; col++)
7860 {
7861 #if GTK_SHEET_DEBUG_DRAW > 0
7862 g_debug("_gtk_sheet_range_draw: (2) %d %d", row, col);
7863 #endif
7864 if (row <= sheet->maxallocrow && col <= sheet->maxalloccol &&
7865 sheet->data[row] && sheet->data[row][col])
7866 {
7867 _cell_draw_background(sheet, row, col);
7868 _cell_draw_label(sheet, row, col);
7869 }
7870 }
7871 }
7872 }
7873
7874 /* draw text right outside range (3) */
7875
7876 if (0 <= drawing_range.coli && drawing_range.coli <= sheet->maxcol)
7877 {
7878 #if GTK_SHEET_DEBUG_DRAW > 0
7879 g_debug("_gtk_sheet_range_draw: (3) row %d - %d col %d - %d",
7880 drawing_range.row0, drawing_range.rowi,
7881 drawing_range.coli + 1, COLPTR(sheet, drawing_range.coli)->right_text_column);
7882 #endif
7883
7884 for (row = drawing_range.row0; row <= drawing_range.rowi; row++)
7885 {
7886 for (col = drawing_range.coli + 1;
7887 col <= COLPTR(sheet, drawing_range.coli)->right_text_column; col++)
7888 {
7889 #if GTK_SHEET_DEBUG_DRAW > 0
7890 g_debug("_gtk_sheet_range_draw: (3) %d %d", row, col);
7891 #endif
7892 _cell_draw_background(sheet, row, col);
7893
7894 if (row <= sheet->maxallocrow && col <= sheet->maxalloccol &&
7895 sheet->data[row] && sheet->data[row][col])
7896 {
7897 _cell_draw_label(sheet, row, col);
7898 }
7899 }
7900 }
7901 }
7902 #endif
7903 gtk_sheet_draw_backing_pixmap(sheet, drawing_range);
7904
7905 if (sheet->state != GTK_SHEET_NORMAL &&
7906 gtk_sheet_range_isvisible(sheet, sheet->range))
7907 {
7908 gtk_sheet_range_draw_selection(sheet, drawing_range);
7909 }
7910
7911 if (activate_active_cell &&
7912 sheet->state == GTK_STATE_NORMAL &&
7913 sheet->active_cell.row >= drawing_range.row0 &&
7914 sheet->active_cell.row <= drawing_range.rowi &&
7915 sheet->active_cell.col >= drawing_range.col0 &&
7916 sheet->active_cell.col <= drawing_range.coli)
7917 {
7918 gtk_sheet_show_active_cell(sheet);
7919 }
7920 }
7921
7922 static void
gtk_sheet_range_draw_selection(GtkSheet * sheet,GtkSheetRange range)7923 gtk_sheet_range_draw_selection(GtkSheet *sheet, GtkSheetRange range)
7924 {
7925 GdkRectangle area;
7926 gint i, j;
7927 GtkSheetRange aux;
7928
7929 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
7930 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
7931 {
7932 #if GTK_SHEET_DEBUG_SELECTION > 0
7933 g_debug("gtk_sheet_range_draw_selection: range outside");
7934 #endif
7935 return;
7936 }
7937
7938 if (!gtk_sheet_range_isvisible(sheet, range))
7939 {
7940 #if GTK_SHEET_DEBUG_SELECTION > 0
7941 g_debug("gtk_sheet_range_draw_selection: range invisible");
7942 #endif
7943 return;
7944 }
7945 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
7946 return;
7947
7948 aux = range;
7949
7950 range.col0 = MAX(sheet->range.col0, range.col0);
7951 range.coli = MIN(sheet->range.coli, range.coli);
7952 range.row0 = MAX(sheet->range.row0, range.row0);
7953 range.rowi = MIN(sheet->range.rowi, range.rowi);
7954
7955 range.col0 = MAX(range.col0, MIN_VIEW_COLUMN(sheet));
7956 range.coli = MIN(range.coli, MAX_VIEW_COLUMN(sheet));
7957 range.row0 = MAX(range.row0, MIN_VIEW_ROW(sheet));
7958 range.rowi = MIN(range.rowi, MAX_VIEW_ROW(sheet));
7959
7960 #if GTK_SHEET_DEBUG_SELECTION > 0
7961 g_debug("gtk_sheet_range_draw_selection: range r %d-%d c %d-%d",
7962 range.row0, range.rowi, range.col0, range.coli);
7963 #endif
7964
7965 for (i = range.row0; i <= range.rowi; i++)
7966 {
7967 if (i > sheet->maxrow)
7968 break;
7969
7970 for (j = range.col0; j <= range.coli; j++)
7971 {
7972 if (j > sheet->maxcol)
7973 break;
7974
7975 if (gtk_sheet_cell_get_state(sheet, i, j) == GTK_STATE_SELECTED &&
7976 GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, j)) &&
7977 GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
7978 {
7979 row_button_set(sheet, i);
7980 _gtk_sheet_column_button_set(sheet, j);
7981
7982 area.x = _gtk_sheet_column_left_xpixel(sheet, j);
7983 area.y = _gtk_sheet_row_top_ypixel(sheet, i);
7984 area.width = COLPTR(sheet, j)->width;
7985 area.height = sheet->row[i].height;
7986
7987 if (i == sheet->range.row0)
7988 {
7989 area.y = area.y + 2;
7990 area.height = area.height - 2;
7991 }
7992 if (i == sheet->range.rowi)
7993 area.height = area.height - 3;
7994
7995 if (j == sheet->range.col0)
7996 {
7997 area.x = area.x + 2;
7998 area.width = area.width - 2;
7999 }
8000 if (j == sheet->range.coli)
8001 area.width = area.width - 3;
8002
8003 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
8004 {
8005 gdk_draw_rectangle(sheet->sheet_window,
8006 sheet->xor_gc,
8007 TRUE,
8008 area.x + 1, area.y + 1,
8009 area.width, area.height);
8010 }
8011 }
8012
8013 }
8014 }
8015
8016 gtk_sheet_draw_border(sheet, sheet->range);
8017 }
8018
8019 static void
gtk_sheet_draw_backing_pixmap(GtkSheet * sheet,GtkSheetRange range)8020 gtk_sheet_draw_backing_pixmap(GtkSheet *sheet, GtkSheetRange range)
8021 {
8022 gint x, y, width, height;
8023
8024 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
8025 return;
8026
8027 x = _gtk_sheet_column_left_xpixel(sheet, range.col0);
8028 y = _gtk_sheet_row_top_ypixel(sheet, range.row0);
8029 width = _gtk_sheet_column_left_xpixel(sheet, range.coli) - x;
8030 if (0 <= range.coli && range.coli <= sheet->maxcol)
8031 width += COLPTR(sheet, range.coli)->width;
8032 height = _gtk_sheet_row_top_ypixel(sheet, range.rowi) - y;
8033 if (0 <= range.rowi && range.rowi <= sheet->maxrow)
8034 height += sheet->row[range.rowi].height;
8035
8036 if (range.row0 == sheet->range.row0)
8037 {
8038 y = y - 5;
8039 height = height + 5;
8040 }
8041 if (range.rowi == sheet->range.rowi)
8042 height = height + 5;
8043
8044 if (range.col0 == sheet->range.col0)
8045 {
8046 x = x - 5;
8047 width = width + 5;
8048 }
8049 if (range.coli == sheet->range.coli)
8050 width = width + 5;
8051
8052 width = MIN(width, sheet->sheet_window_width - x);
8053 height = MIN(height, sheet->sheet_window_height - y);
8054
8055 x--;
8056 y--;
8057 width += 2;
8058 height += 2;
8059
8060 x = (sheet->row_titles_visible) ? MAX(x, sheet->row_title_area.width) : MAX(x, 0);
8061 y = (sheet->column_titles_visible) ? MAX(y, sheet->column_title_area.height) : MAX(y, 0);
8062
8063 if (range.coli >= sheet->maxcol)
8064 width = sheet->sheet_window_width - x;
8065 if (range.rowi >= sheet->maxrow)
8066 height = sheet->sheet_window_height - y;
8067
8068 #if GTK_SHEET_DEBUG_EXPOSE > 0
8069 g_debug("gtk_sheet_draw_backing_pixmap: x %d y %d w %d h %d",
8070 x, y, width + 1, height + 1);
8071 #endif
8072
8073 gdk_draw_pixmap(sheet->sheet_window,
8074 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
8075 sheet->pixmap,
8076 x, y,
8077 x, y,
8078 width + 1,
8079 height + 1);
8080 }
8081
8082 static inline void
gtk_sheet_cell_init(GtkSheetCell * cell)8083 gtk_sheet_cell_init(GtkSheetCell *cell)
8084 {
8085 cell->extent.x = cell->extent.y = 0;
8086 cell->extent.width = cell->extent.height = 0;
8087
8088 cell->row = cell->col = -1;
8089
8090 cell->attributes = NULL;
8091 cell->text = cell->link = NULL;
8092
8093 cell->tooltip_markup = cell->tooltip_text = NULL;
8094 }
8095
8096 static void
gtk_sheet_cell_finalize(GtkSheet * sheet,GtkSheetCell * cell)8097 gtk_sheet_cell_finalize(GtkSheet *sheet, GtkSheetCell *cell)
8098 {
8099 g_return_if_fail(cell != NULL);
8100
8101 if (cell->text)
8102 {
8103 g_free(cell->text);
8104 cell->text = NULL;
8105
8106 if (GTK_IS_OBJECT(sheet) && G_OBJECT(sheet)->ref_count > 0)
8107 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[CLEAR_CELL], 0,
8108 cell->row, cell->col);
8109 }
8110
8111 if (cell->link)
8112 {
8113 cell->link = NULL;
8114 }
8115
8116 if (cell->tooltip_markup)
8117 {
8118 g_free(cell->tooltip_markup);
8119 cell->tooltip_markup = NULL;
8120 }
8121
8122 if (cell->tooltip_text)
8123 {
8124 g_free(cell->tooltip_text);
8125 cell->tooltip_text = NULL;
8126 }
8127
8128 if (cell->attributes)
8129 {
8130 GtkSheetCellAttr *attributes = cell->attributes;
8131
8132 if (attributes->font_desc && attributes->do_font_desc_free)
8133 {
8134 pango_font_description_free(attributes->font_desc); /* free */
8135 attributes->font_desc = NULL;
8136 }
8137 g_free(attributes);
8138
8139 cell->attributes = NULL;
8140 }
8141 }
8142
8143 static GtkSheetCell *
gtk_sheet_cell_new(void)8144 gtk_sheet_cell_new(void)
8145 {
8146 GtkSheetCell *cell = g_new(GtkSheetCell, 1);
8147 gtk_sheet_cell_init(cell);
8148 return (cell);
8149 }
8150
8151 /**
8152 * gtk_sheet_set_cell_text:
8153 * @sheet: a #GtkSheet.
8154 * @row: row_number
8155 * @col: column number
8156 * @text: cell text
8157 *
8158 * Set cell contents and allocate memory if needed. No
8159 * justifcation is made. attributes and links remain unchanged.
8160 */
8161 void
gtk_sheet_set_cell_text(GtkSheet * sheet,gint row,gint col,const gchar * text)8162 gtk_sheet_set_cell_text(GtkSheet *sheet, gint row, gint col, const gchar *text)
8163 {
8164 g_return_if_fail(sheet != NULL);
8165 g_return_if_fail(GTK_IS_SHEET(sheet));
8166
8167 if (col > sheet->maxcol || row > sheet->maxrow)
8168 return;
8169 if (col < 0 || row < 0)
8170 return;
8171
8172 GtkSheetCellAttr attributes;
8173 gtk_sheet_get_attributes(sheet, row, col, &attributes);
8174 gtk_sheet_set_cell(sheet, row, col, attributes.justification, text);
8175 }
8176
8177 /**
8178 * gtk_sheet_set_cell:
8179 * @sheet: a #GtkSheet.
8180 * @row: row_number
8181 * @col: column number
8182 * @justification: a #GtkJustification :GTK_JUSTIFY_LEFT, RIGHT, CENTER
8183 * @text: cell text
8184 *
8185 * Set cell contents and allocate memory if needed.
8186 */
8187 void
gtk_sheet_set_cell(GtkSheet * sheet,gint row,gint col,GtkJustification justification,const gchar * text)8188 gtk_sheet_set_cell(GtkSheet *sheet, gint row, gint col,
8189 GtkJustification justification,
8190 const gchar *text)
8191 {
8192 GtkSheetCell *cell;
8193
8194 g_return_if_fail(sheet != NULL);
8195 g_return_if_fail(GTK_IS_SHEET(sheet));
8196 if (col > sheet->maxcol || row > sheet->maxrow)
8197 return;
8198 if (col < 0 || row < 0)
8199 return;
8200
8201 #if GTK_SHEET_DEBUG_SET_CELL_TIMER > 0
8202 GTimer *tm = g_timer_new();
8203 #endif
8204
8205 CheckCellData(sheet, row, col);
8206
8207 #if 0 && GTK_SHEET_DEBUG_SET_CELL_TIMER > 0
8208 g_debug("st1: %0.6f", g_timer_elapsed(tm, NULL));
8209 #endif
8210
8211 cell = sheet->data[row][col];
8212
8213 GtkSheetCellAttr attributes;
8214 gtk_sheet_get_attributes(sheet, row, col, &attributes);
8215 attributes.justification = justification;
8216 gtk_sheet_set_cell_attributes(sheet, row, col, attributes);
8217
8218 if (cell->text)
8219 {
8220 g_free(cell->text);
8221 cell->text = NULL;
8222 }
8223
8224 if (text)
8225 {
8226 gchar *dataformat = gtk_sheet_column_get_format(sheet, col);
8227
8228 if (dataformat)
8229 text = gtk_data_format_remove(text, dataformat);
8230
8231 #if GTK_SHEET_DEBUG_SET_CELL_TEXT > 0
8232 g_debug("gtk_sheet_set_cell[%p]: r %d c %d ar %d ac %d <%s>",
8233 sheet, row, col, sheet->active_cell.row, sheet->active_cell.col, text);
8234 #endif
8235
8236 cell->text = g_strdup(text);
8237 }
8238 #if GTK_SHEET_DEBUG_SET_CELL_TEXT > 0
8239 else
8240 g_debug("gtk_sheet_set_cell[%p]: r %d c %d ar %d ac %d NULL",
8241 sheet, row, col, sheet->active_cell.row, sheet->active_cell.col);
8242 #endif
8243
8244 #if 0 && GTK_SHEET_DEBUG_SET_CELL_TIMER > 0
8245 g_debug("st2: %0.6f", g_timer_elapsed(tm, NULL));
8246 #endif
8247
8248 _gtk_sheet_update_extent(sheet, cell, row, col);
8249
8250 #if GTK_SHEET_DEBUG_SET_CELL_TIMER > 0
8251 g_debug("st3: %0.6f", g_timer_elapsed(tm, NULL));
8252 #endif
8253
8254 if (attributes.is_visible)
8255 {
8256 gboolean need_draw = TRUE;
8257
8258 /* PR#104553 - if sheet entry editor is active on the cell being modified,
8259 we need to update it's contents
8260 */
8261 if (row == sheet->active_cell.row && col == sheet->active_cell.col)
8262 {
8263 #if GTK_SHEET_DEBUG_SET_CELL_TEXT > 0
8264 g_debug("gtk_sheet_set_cell[%p]: update sheet entry", sheet);
8265 #endif
8266 gtk_sheet_set_entry_text(sheet, text); /* PR#104553 */
8267 }
8268
8269 if (gtk_sheet_autoresize(sheet)) /* handle immediate resize */
8270 {
8271 if (cell->text && cell->text[0])
8272 {
8273 if (gtk_sheet_autoresize_columns(sheet))
8274 {
8275 GtkSheetColumn *colptr = COLPTR(sheet, col);
8276 gint new_width = COLUMN_EXTENT_TO_WIDTH(colptr->max_extent_width);
8277
8278 if (new_width != colptr->width)
8279 {
8280 #if GTK_SHEET_DEBUG_SIZE > 0
8281 g_debug("gtk_sheet_set_cell[%d]: set col width %d", col, new_width);
8282 #endif
8283 gtk_sheet_set_column_width(sheet, col, new_width);
8284 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_REDRAW_PENDING);
8285 need_draw = FALSE;
8286 }
8287 }
8288
8289 if (gtk_sheet_autoresize_rows(sheet))
8290 {
8291 GtkSheetRow *rowptr = ROWPTR(sheet, row);
8292 gint new_height = ROW_EXTENT_TO_HEIGHT(rowptr->max_extent_height);
8293
8294 if (new_height != rowptr->height)
8295 {
8296 #if GTK_SHEET_DEBUG_SIZE > 0
8297 g_debug("gtk_sheet_set_cell[%d]: set row height %d", row, new_height);
8298 #endif
8299 gtk_sheet_set_row_height(sheet, row, new_height);
8300 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_REDRAW_PENDING);
8301 need_draw = FALSE;
8302 }
8303 }
8304 }
8305 }
8306
8307 if (need_draw)
8308 {
8309 GtkSheetRange range;
8310
8311 range.row0 = row;
8312 range.rowi = row;
8313 range.col0 = sheet->view.col0;
8314 range.coli = sheet->view.coli;
8315
8316 if (!GTK_SHEET_IS_FROZEN(sheet))
8317 _gtk_sheet_range_draw(sheet, &range, TRUE);
8318 }
8319 }
8320 #if GTK_SHEET_DEBUG_SET_CELL_TIMER > 0
8321 g_debug("st4: %0.6f", g_timer_elapsed(tm, NULL));
8322 #endif
8323
8324 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[CHANGED], 0, row, col);
8325
8326 #if GTK_SHEET_DEBUG_SET_CELL_TIMER > 0
8327 g_debug("st9: %0.6f", g_timer_elapsed(tm, NULL));
8328 g_timer_destroy(tm);
8329 #endif
8330 }
8331
8332 /**
8333 * gtk_sheet_cell_clear:
8334 * @sheet: a #GtkSheet.
8335 * @row: row_number
8336 * @column: column number
8337 *
8338 * Clear cell contents.
8339 */
8340 void
gtk_sheet_cell_clear(GtkSheet * sheet,gint row,gint column)8341 gtk_sheet_cell_clear(GtkSheet *sheet, gint row, gint column)
8342 {
8343 GtkSheetRange range;
8344
8345 g_return_if_fail(sheet != NULL);
8346 g_return_if_fail(GTK_IS_SHEET(sheet));
8347
8348 if (column > sheet->maxcol || row > sheet->maxrow)
8349 return;
8350 if (column > sheet->maxalloccol || row > sheet->maxallocrow)
8351 return;
8352 if (column < 0 || row < 0)
8353 return;
8354
8355 range.row0 = row;
8356 range.rowi = row;
8357 range.col0 = sheet->view.col0;
8358 range.coli = sheet->view.coli;
8359
8360 gtk_sheet_real_cell_clear(sheet, row, column, FALSE);
8361
8362 if (!GTK_SHEET_IS_FROZEN(sheet))
8363 {
8364 _gtk_sheet_range_draw(sheet, &range, TRUE);
8365 }
8366 }
8367
8368 /**
8369 * gtk_sheet_cell_delete:
8370 * @sheet: a #GtkSheet.
8371 * @row: row_number
8372 * @column: column number
8373 *
8374 * Clear cell contents and remove links.
8375 */
8376 void
gtk_sheet_cell_delete(GtkSheet * sheet,gint row,gint column)8377 gtk_sheet_cell_delete(GtkSheet *sheet, gint row, gint column)
8378 {
8379 GtkSheetRange range;
8380
8381 g_return_if_fail(sheet != NULL);
8382 g_return_if_fail(GTK_IS_SHEET(sheet));
8383 if (column > sheet->maxcol || row > sheet->maxrow)
8384 return;
8385 if (column > sheet->maxalloccol || row > sheet->maxallocrow)
8386 return;
8387 if (column < 0 || row < 0)
8388 return;
8389
8390 range.row0 = row;
8391 range.rowi = row;
8392 range.col0 = sheet->view.col0;
8393 range.coli = sheet->view.coli;
8394
8395 gtk_sheet_real_cell_clear(sheet, row, column, TRUE);
8396
8397 if (!GTK_SHEET_IS_FROZEN(sheet))
8398 _gtk_sheet_range_draw(sheet, &range, TRUE);
8399 }
8400
8401 static void
gtk_sheet_real_cell_clear(GtkSheet * sheet,gint row,gint column,gboolean delete)8402 gtk_sheet_real_cell_clear(GtkSheet *sheet,
8403 gint row, gint column,
8404 gboolean delete)
8405 {
8406 GtkSheetCell *cell;
8407
8408 if (row > sheet->maxallocrow || column > sheet->maxalloccol)
8409 return;
8410
8411 if (!sheet->data[row])
8412 return;
8413
8414 cell = sheet->data[row][column];
8415 if (!cell)
8416 return;
8417
8418 #if GTK_SHEET_DEBUG_SET_CELL_TEXT > 0
8419 g_debug("gtk_sheet_real_cell_clear[%p]: r %d c %d ar %d ac %d <%s>",
8420 sheet, row, column,
8421 sheet->active_cell.row, sheet->active_cell.col,
8422 cell->text ? cell->text : "<NULL>");
8423 #endif
8424
8425
8426 if (cell->text)
8427 {
8428 g_free(cell->text);
8429 cell->text = NULL;
8430
8431 if (GTK_IS_OBJECT(sheet) && G_OBJECT(sheet)->ref_count > 0)
8432 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[CLEAR_CELL], 0, row, column);
8433 }
8434
8435 if (cell->link)
8436 {
8437 cell->link = NULL;
8438 }
8439
8440 if (cell->tooltip_markup)
8441 {
8442 g_free(cell->tooltip_markup);
8443 cell->tooltip_markup = NULL;
8444 }
8445
8446 if (cell->tooltip_text)
8447 {
8448 g_free(cell->tooltip_text);
8449 cell->tooltip_text = NULL;
8450 }
8451
8452 if (delete)
8453 {
8454 gtk_sheet_cell_finalize(sheet, cell);
8455
8456 #if GTK_SHEET_DEBUG_ALLOCATION > 0
8457 g_debug("gtk_sheet_real_cell_clear: freeing %d %d", row, column);
8458 #endif
8459
8460 g_free(cell);
8461 sheet->data[row][column] = NULL;
8462 }
8463 }
8464
8465 /**
8466 * gtk_sheet_range_clear:
8467 * @sheet: a #GtkSheet.
8468 * @range: a #GtkSheetRange
8469 *
8470 * Clear range contents. If range==NULL the whole sheet will be cleared.
8471 */
8472 void
gtk_sheet_range_clear(GtkSheet * sheet,const GtkSheetRange * range)8473 gtk_sheet_range_clear(GtkSheet *sheet, const GtkSheetRange *range)
8474 {
8475 g_return_if_fail(sheet != NULL);
8476 g_return_if_fail(GTK_IS_SHEET(sheet));
8477
8478 gtk_sheet_real_range_clear(sheet, range, FALSE);
8479 }
8480
8481 /**
8482 * gtk_sheet_range_delete:
8483 * @sheet: a #GtkSheet.
8484 * @range: a #GtkSheetRange
8485 *
8486 * Clear range contents and remove links.
8487 * FIXME:: if range==NULL whole sheet is deleted?
8488 */
8489 void
gtk_sheet_range_delete(GtkSheet * sheet,const GtkSheetRange * range)8490 gtk_sheet_range_delete(GtkSheet *sheet, const GtkSheetRange *range)
8491 {
8492 g_return_if_fail(sheet != NULL);
8493 g_return_if_fail(GTK_IS_SHEET(sheet));
8494
8495 gtk_sheet_real_range_clear(sheet, range, TRUE);
8496 }
8497
8498 static void
gtk_sheet_real_range_clear(GtkSheet * sheet,const GtkSheetRange * range,gboolean delete)8499 gtk_sheet_real_range_clear(GtkSheet *sheet, const GtkSheetRange *range,
8500 gboolean delete)
8501 {
8502 gint row, col;
8503 GtkSheetRange clear;
8504
8505 if (!range)
8506 {
8507 clear.row0 = 0;
8508 clear.rowi = sheet->maxallocrow;
8509 clear.col0 = 0;
8510 clear.coli = sheet->maxalloccol;
8511 }
8512 else
8513 {
8514 clear = *range;
8515 }
8516
8517 clear.row0 = MAX(clear.row0, 0);
8518 clear.col0 = MAX(clear.col0, 0);
8519 clear.rowi = MIN(clear.rowi, sheet->maxallocrow);
8520 clear.coli = MIN(clear.coli, sheet->maxalloccol);
8521
8522 for (row = clear.row0; row <= clear.rowi; row++)
8523 {
8524 for (col = clear.col0; col <= clear.coli; col++)
8525 {
8526 gtk_sheet_real_cell_clear(sheet, row, col, delete);
8527 }
8528 _gtk_sheet_recalc_extent_height(sheet, row);
8529 }
8530 for (col = clear.col0; col <= clear.coli; col++)
8531 {
8532 _gtk_sheet_recalc_extent_width(sheet, col);
8533 }
8534
8535 _gtk_sheet_range_draw(sheet, NULL, TRUE);
8536 }
8537
8538 /**
8539 * gtk_sheet_cell_get_text:
8540 * @sheet: a #GtkSheet
8541 * @row: row number
8542 * @col: column number
8543 *
8544 * Get cell text.
8545 *
8546 * Returns: a pointer to the cell text, or NULL.
8547 * Do not modify or free it.
8548 */
8549 gchar *
gtk_sheet_cell_get_text(GtkSheet * sheet,gint row,gint col)8550 gtk_sheet_cell_get_text (GtkSheet *sheet, gint row, gint col)
8551 {
8552 g_return_val_if_fail(sheet != NULL, NULL);
8553 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
8554
8555 if (col > sheet->maxcol || row > sheet->maxrow)
8556 return (NULL);
8557 if (col < 0 || row < 0)
8558 return (NULL);
8559
8560 if (row > sheet->maxallocrow || col > sheet->maxalloccol)
8561 return (NULL);
8562
8563 if (!sheet->data[row])
8564 return (NULL);
8565 if (!sheet->data[row][col])
8566 return (NULL);
8567 if (!sheet->data[row][col]->text)
8568 return (NULL);
8569 if (!sheet->data[row][col]->text[0])
8570 return (NULL);
8571
8572 return (sheet->data[row][col]->text);
8573 }
8574
8575 /**
8576 * gtk_sheet_link_cell:
8577 * @sheet: a #GtkSheet
8578 * @row: row number
8579 * @col: column number
8580 * @link: pointer linked to the cell
8581 *
8582 * Link pointer to a cell.
8583 */
8584 void
gtk_sheet_link_cell(GtkSheet * sheet,gint row,gint col,gpointer link)8585 gtk_sheet_link_cell(GtkSheet *sheet, gint row, gint col, gpointer link)
8586 {
8587 g_return_if_fail(sheet != NULL);
8588 g_return_if_fail(GTK_IS_SHEET(sheet));
8589
8590 if (col > sheet->maxcol || row > sheet->maxrow)
8591 return;
8592 if (col < 0 || row < 0)
8593 return;
8594
8595 CheckCellData(sheet, row, col);
8596
8597 sheet->data[row][col]->link = link;
8598 }
8599
8600 /**
8601 * gtk_sheet_get_link:
8602 * @sheet: a #GtkSheet
8603 * @row: row number
8604 * @col: column number
8605 *
8606 * Get link pointer from a cell.
8607 *
8608 * Returns: (transfer none) pointer linked to the cell
8609 */
8610 gpointer
gtk_sheet_get_link(GtkSheet * sheet,gint row,gint col)8611 gtk_sheet_get_link(GtkSheet *sheet, gint row, gint col)
8612 {
8613 g_return_val_if_fail(sheet != NULL, NULL);
8614 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
8615 if (col > sheet->maxcol || row > sheet->maxrow)
8616 return (NULL);
8617 if (col < 0 || row < 0)
8618 return (NULL);
8619
8620 if (row > sheet->maxallocrow || col > sheet->maxalloccol)
8621 return (NULL);
8622 if (!sheet->data[row])
8623 return (NULL); /* Added by Chris Howell */
8624 if (!sheet->data[row][col])
8625 return (NULL); /* Added by Bob Lissner */
8626
8627 return (sheet->data[row][col]->link);
8628 }
8629
8630 /**
8631 * gtk_sheet_remove_link:
8632 * @sheet: a #GtkSheet
8633 * @row: row number
8634 * @col: column number
8635 *
8636 * Remove link pointer from a cell.
8637 */
8638 void
gtk_sheet_remove_link(GtkSheet * sheet,gint row,gint col)8639 gtk_sheet_remove_link(GtkSheet *sheet, gint row, gint col)
8640 {
8641 g_return_if_fail(sheet != NULL);
8642 g_return_if_fail(GTK_IS_SHEET(sheet));
8643 if (col > sheet->maxcol || row > sheet->maxrow)
8644 return;
8645 if (col < 0 || row < 0)
8646 return;
8647
8648 /* Fixed by Andreas Voegele */
8649 if (row <= sheet->maxallocrow && col <= sheet->maxalloccol &&
8650 sheet->data[row] && sheet->data[row][col] &&
8651 sheet->data[row][col]->link)
8652 sheet->data[row][col]->link = NULL;
8653 }
8654
8655 /**
8656 * gtk_sheet_cell_get_state:
8657 * @sheet: a #GtkSheet
8658 * @row: row number
8659 * @col: column number
8660 *
8661 * Get status of a cell.
8662 *
8663 * Returns: a #GtkStateType:
8664 * GTK_SHEET_NORMAL,GTK_SHEET_ROW_SELECTED,GTK_SHEET_COLUMN_SELECTED,GTK_SHEET_RANGE_SELECTED
8665 */
8666 GtkStateType
gtk_sheet_cell_get_state(GtkSheet * sheet,gint row,gint col)8667 gtk_sheet_cell_get_state(GtkSheet *sheet, gint row, gint col)
8668 {
8669 gint state;
8670 GtkSheetRange *range;
8671
8672 g_return_val_if_fail(sheet != NULL, 0);
8673 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
8674 if (col > sheet->maxcol || row > sheet->maxrow)
8675 return (0);
8676 if (col < 0 || row < 0)
8677 return (0);
8678
8679 state = sheet->state;
8680 range = &sheet->range;
8681
8682 switch(state)
8683 {
8684 case GTK_SHEET_NORMAL:
8685 return (GTK_STATE_NORMAL);
8686 break;
8687 case GTK_SHEET_ROW_SELECTED:
8688 if (row >= range->row0 && row <= range->rowi)
8689 return (GTK_STATE_SELECTED);
8690 break;
8691 case GTK_SHEET_COLUMN_SELECTED:
8692 if (col >= range->col0 && col <= range->coli)
8693 return (GTK_STATE_SELECTED);
8694 break;
8695 case GTK_SHEET_RANGE_SELECTED:
8696 if (row >= range->row0 && row <= range->rowi &&\
8697 col >= range->col0 && col <= range->coli)
8698 return (GTK_STATE_SELECTED);
8699 break;
8700 }
8701 return (GTK_STATE_NORMAL);
8702 }
8703
8704 /**
8705 * gtk_sheet_get_pixel_info:
8706 * @sheet: a #GtkSheet
8707 * @window: base window for coordinates (null)
8708 * @x: x coordinate
8709 * @y: y coordinate
8710 * @row: cell row number
8711 * @column: cell column number
8712 *
8713 * Get row and column correspondig to the given position within
8714 * the sheet.
8715 *
8716 * In order to decode clicks into to title area correctly, pass
8717 * the GdkWindow from the button event. Omitting the
8718 * window (NULL) defaults to the sheet window.
8719 *
8720 * row and column may return values in the range [-1 .. max+1]
8721 * depending on wether the position lies within the title area,
8722 * the sheet cell area or beyond the outermost row/column.
8723 *
8724 * All 9 sheet areas can be reliably determined by evaluating
8725 * the returned row/column values (title area/cell
8726 * area/outside).
8727 *
8728 * Returns: TRUE if the position lies within the sheet cell area
8729 * or FALSE when outside (title area f.e.)
8730 */
8731 gboolean
gtk_sheet_get_pixel_info(GtkSheet * sheet,GdkWindow * window,gint x,gint y,gint * row,gint * column)8732 gtk_sheet_get_pixel_info(GtkSheet *sheet,
8733 GdkWindow *window, gint x, gint y, gint *row, gint *column)
8734 {
8735 gint trow, tcol;
8736
8737 *row = *column = -1; /* init all output vars */
8738
8739 g_return_val_if_fail(sheet != NULL, 0);
8740 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
8741
8742 /* there is a coordinate shift to be considered when
8743 clicking into a title area because:
8744 - the sheet window covers the whole sheet
8745 - the column titles window overlaps the sheet window,
8746 but may be shifted right when row titles are visible
8747 - the row titles window overlaps the sheet window,
8748 but may be shifted down when column titles are visible
8749 */
8750
8751 if (sheet->column_titles_visible
8752 && window == sheet->column_title_window)
8753 {
8754 if (sheet->row_titles_visible)
8755 {
8756 #if GTK_SHEET_DEBUG_PIXEL_INFO > 0
8757 g_debug("gtk_sheet_get_pixel_info: shift x");
8758 #endif
8759 x += sheet->row_title_area.width;
8760 }
8761 #if GTK_SHEET_DEBUG_PIXEL_INFO > 0
8762 g_debug("gtk_sheet_get_pixel_info: r1");
8763 #endif
8764 trow = -1;
8765 tcol = _gtk_sheet_column_from_xpixel(sheet, x);
8766 }
8767 else if (sheet->row_titles_visible
8768 && window == sheet->row_title_window)
8769 {
8770 if (sheet->column_titles_visible)
8771 {
8772 #if GTK_SHEET_DEBUG_PIXEL_INFO > 0
8773 g_debug("gtk_sheet_get_pixel_info: shift y");
8774 #endif
8775 y += sheet->column_title_area.height;
8776 }
8777 #if GTK_SHEET_DEBUG_PIXEL_INFO > 0
8778 g_debug("gtk_sheet_get_pixel_info: c1");
8779 #endif
8780 trow = _gtk_sheet_row_from_ypixel(sheet, y);
8781 tcol = -1;
8782 }
8783 else if (sheet->column_titles_visible
8784 && sheet->row_titles_visible
8785 && x < sheet->row_title_area.width
8786 && y < sheet->column_title_area.height )
8787 {
8788 #if GTK_SHEET_DEBUG_PIXEL_INFO > 0
8789 g_debug("gtk_sheet_get_pixel_info: sb");
8790 #endif
8791 trow = -1;
8792 tcol = -1;
8793 }
8794 else
8795 {
8796 #if GTK_SHEET_DEBUG_PIXEL_INFO > 0
8797 g_debug("gtk_sheet_get_pixel_info: rN/cN");
8798 #endif
8799 trow = _gtk_sheet_row_from_ypixel(sheet, y);
8800 tcol = _gtk_sheet_column_from_xpixel(sheet, x);
8801 }
8802
8803 #if GTK_SHEET_DEBUG_PIXEL_INFO > 0
8804 g_debug("gtk_sheet_get_pixel_info: x %d y %d row %d col %d", x, y, trow, tcol);
8805 #endif
8806
8807 *row = trow;
8808 *column = tcol;
8809
8810 /* bounds checking, return false if the user clicked
8811 * on a blank area */
8812
8813 if (trow < 0 || trow > sheet->maxrow)
8814 return (FALSE);
8815 if (tcol < 0 || tcol > sheet->maxcol)
8816 return (FALSE);
8817
8818 return (TRUE);
8819 }
8820
8821 /**
8822 * gtk_sheet_get_cell_area:
8823 * @sheet: a #GtkSheet
8824 * @row: row number
8825 * @column: column number
8826 * @area: a #GdkRectangle area of the cell
8827 *
8828 * Get area of a given cell.
8829 *
8830 * Returns: TRUE(success) or FALSE(failure)
8831 */
8832 gboolean
gtk_sheet_get_cell_area(GtkSheet * sheet,gint row,gint col,GdkRectangle * area)8833 gtk_sheet_get_cell_area(GtkSheet *sheet,
8834 gint row,
8835 gint col,
8836 GdkRectangle *area)
8837 {
8838 g_return_val_if_fail(sheet != NULL, 0);
8839 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
8840
8841 if (row > sheet->maxrow || col > sheet->maxcol)
8842 return (FALSE);
8843
8844 area->x = (col == -1) ? 0 : (_gtk_sheet_column_left_xpixel(sheet, col) -
8845 (sheet->row_titles_visible ? sheet->row_title_area.width : 0));
8846 area->y = (row == -1) ? 0 : (_gtk_sheet_row_top_ypixel(sheet, row) -
8847 (sheet->column_titles_visible ? sheet->column_title_area.height : 0));
8848 area->width = (col == -1) ? sheet->row_title_area.width : COLPTR(sheet, col)->width;
8849 area->height = (row == -1) ? sheet->column_title_area.height : sheet->row[row].height;
8850
8851 /*
8852 if(row < 0 || col < 0) return FALSE;
8853
8854 area->x = COLUMN_LEFT_XPIXEL(sheet, col);
8855 area->y = ROW_TOP_YPIXEL(sheet, row);
8856 if(sheet->row_titles_visible)
8857 area->x -= sheet->row_title_area.width;
8858 if(sheet->column_titles_visible)
8859 area->y -= sheet->column_title_area.height;
8860
8861 area->width = COLPTR(sheet, col)->width;
8862 area->height = sheet->row[row].height;
8863 */
8864 return (TRUE);
8865 }
8866
8867 /**
8868 * gtk_sheet_set_active_cell:
8869 * @sheet: a #GtkSheet
8870 * @row: row number
8871 * @column: column number
8872 *
8873 * Set active cell where the cell entry will be displayed.
8874 * Use (row,col) = (-1,-1) to deactivate active cell.
8875 *
8876 * Returns: FALSE if current cell can't be deactivated or
8877 * requested cell can't be activated
8878 */
8879 gboolean
gtk_sheet_set_active_cell(GtkSheet * sheet,gint row,gint col)8880 gtk_sheet_set_active_cell(GtkSheet *sheet, gint row, gint col)
8881 {
8882 g_return_val_if_fail(sheet != NULL, 0);
8883 g_return_val_if_fail(GTK_IS_SHEET(sheet), 0);
8884
8885 if (row > sheet->maxrow || col > sheet->maxcol)
8886 return (FALSE);
8887
8888 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
8889 g_debug("gtk_sheet_set_active_cell: row %d col %d", row, col);
8890 #endif
8891
8892 if (!gtk_widget_get_can_focus(GTK_WIDGET(sheet)))
8893 {
8894 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
8895 g_debug("gtk_sheet_set_active_cell: row %d col %d abort: sheet, can-focus false", row, col);
8896 #endif
8897 return (FALSE);
8898 }
8899
8900 if (col >= 0 && !GTK_SHEET_COLUMN_CAN_FOCUS(COLPTR(sheet, col)))
8901 {
8902 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
8903 g_debug("gtk_sheet_set_active_cell: row %d col %d abort: sheet column, can-focus false", row, col);
8904 #endif
8905 return (FALSE);
8906 }
8907
8908 if (col >= 0 && !GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)))
8909 {
8910 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
8911 g_debug("gtk_sheet_set_active_cell: row %d col %d abort: sheet column, visible false", row, col);
8912 #endif
8913 return (FALSE);
8914 }
8915
8916 if (gtk_widget_get_realized(GTK_WIDGET(sheet)))
8917 {
8918 #if 0
8919 gint old_row = sheet->active_cell.row;
8920 gint old_col = sheet->active_cell.col;
8921 #endif
8922
8923 if (!gtk_sheet_deactivate_cell(sheet))
8924 {
8925 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
8926 g_debug("gtk_sheet_set_active_cell: abort: deactivation false");
8927 #endif
8928 return (FALSE);
8929 }
8930
8931 /* the deactivate signal handler may have activated another cell here */
8932 #if 0
8933 if ((sheet->active_cell.row != old_row) || (sheet->active_cell.col != old_col))
8934 {
8935 #ifdef GTK_SHEET_DEBUG
8936 g_debug("gtk_sheet_set_active_cell: deactivation moved active cell to row %d col %d",
8937 sheet->active_cell.row, sheet->active_cell.col);
8938 #endif
8939 return(FALSE);
8940 }
8941 #endif
8942 }
8943
8944 if (row < 0 || col < 0)
8945 {
8946 sheet->range.row0 = -1;
8947 sheet->range.rowi = -1;
8948 sheet->range.col0 = -1;
8949 sheet->range.coli = -1;
8950
8951 return (TRUE);
8952 }
8953
8954 sheet->active_cell.row = row;
8955 sheet->active_cell.col = col;
8956
8957 if (!gtk_sheet_activate_cell(sheet, row, col))
8958 return (FALSE);
8959
8960 _gtk_sheet_move_query(sheet, row, col, TRUE);
8961
8962 return (TRUE);
8963 }
8964
8965 /**
8966 * gtk_sheet_get_active_cell:
8967 * @sheet: a #GtkSheet
8968 * @row: row number
8969 * @column: column number
8970 *
8971 * Store the coordinates of the active cell in row,col.
8972 * If (row<0 || col<0) then there was no active cell in the
8973 * sheet.
8974 */
8975 void
gtk_sheet_get_active_cell(GtkSheet * sheet,gint * row,gint * column)8976 gtk_sheet_get_active_cell(GtkSheet *sheet, gint *row, gint *column)
8977 {
8978 g_return_if_fail(sheet != NULL);
8979 g_return_if_fail(GTK_IS_SHEET(sheet));
8980
8981 *row = sheet->active_cell.row;
8982 *column = sheet->active_cell.col;
8983 }
8984
8985 /**
8986 * gtk_sheet_set_tab_direction:
8987 * @sheet: a #GtkSheet
8988 * @dir: the primary tab direction
8989 *
8990 * Sets a primary movement direction to the Tab, Return and
8991 * Enter keys, and assigns the opposite direction to the same
8992 * keys with GDK_SHIFT_MASK.
8993 *
8994 * Transposed movement direction can be accessed with
8995 * GTK_SHEET_MOD_MASK|GDK_CONTROL_MASK and
8996 * GTK_SHEET_MOD_MASK|GDK_CONTROL_MASK|GDK_SHIFT_MASK.
8997 *
8998 * All bindings are defined for the #GtkSheetClass, so all sheet
8999 * instances use the same movement directions.
9000 *
9001 * Default: GTK_DIR_TAB_FORWARD.
9002 *
9003 * Since: 3.0.2
9004 */
9005 void
gtk_sheet_set_tab_direction(GtkSheet * sheet,GtkDirectionType dir)9006 gtk_sheet_set_tab_direction(GtkSheet *sheet, GtkDirectionType dir)
9007 {
9008 GtkSheetClass *klass;
9009
9010 g_return_if_fail(sheet != NULL);
9011 g_return_if_fail(GTK_IS_SHEET(sheet));
9012
9013 klass = GTK_SHEET_GET_CLASS(sheet);
9014
9015 _gtk_sheet_class_init_tab_bindings(klass, dir);
9016 }
9017
9018
9019
9020 /*
9021 * gtk_sheet_entry_changed_handler:
9022 *
9023 * this is the sheet_entry editable "changed" signal handler
9024 *
9025 * The signal is emited when typing into the entry, or changing
9026 * its content.
9027 *
9028 * Its main purpose is to update the cell data text
9029 *
9030 * @param widget the sheet_entry widget
9031 * @param data the #GtkSheet, passed on signal creation
9032 */
9033 static void
gtk_sheet_entry_changed_handler(GtkWidget * widget,gpointer data)9034 gtk_sheet_entry_changed_handler(GtkWidget *widget, gpointer data)
9035 {
9036 GtkSheet *sheet;
9037 gint row, col;
9038 char *text;
9039
9040 g_return_if_fail(data != NULL);
9041 g_return_if_fail(GTK_IS_SHEET(data));
9042
9043 sheet = GTK_SHEET(data);
9044
9045 if (!gtk_widget_get_visible(gtk_sheet_get_entry_widget(sheet)))
9046 return;
9047 if (sheet->state != GTK_STATE_NORMAL)
9048 return;
9049
9050 row = sheet->active_cell.row;
9051 col = sheet->active_cell.col;
9052
9053 if (row < 0 || col < 0)
9054 return;
9055
9056 sheet->active_cell.row = -1;
9057 sheet->active_cell.col = -1;
9058
9059 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IS_FROZEN);
9060
9061 text = gtk_sheet_get_entry_text(sheet);
9062 gtk_sheet_set_cell_text(sheet, row, col, text);
9063 g_free(text);
9064
9065 if (sheet->freeze_count == 0)
9066 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN);
9067
9068 sheet->active_cell.row = row;;
9069 sheet->active_cell.col = col;
9070 }
9071
9072
9073 static gboolean
gtk_sheet_deactivate_cell(GtkSheet * sheet)9074 gtk_sheet_deactivate_cell(GtkSheet *sheet)
9075 {
9076 gboolean veto = TRUE;
9077 gint row, col;
9078
9079 g_return_val_if_fail(sheet != NULL, FALSE);
9080 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
9081
9082 row = sheet->active_cell.row;
9083 col = sheet->active_cell.col;
9084
9085 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9086 g_debug("gtk_sheet_deactivate_cell: called, row %d col %d", row, col);
9087 #endif
9088
9089 if (row < 0 || row > sheet->maxrow)
9090 return (TRUE);
9091 if (col < 0 || col > sheet->maxcol)
9092 return (TRUE);
9093
9094 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
9095 return (FALSE);
9096 if (sheet->state != GTK_SHEET_NORMAL)
9097 return (FALSE);
9098
9099 gtk_sheet_entry_signal_disconnect_by_func(sheet,
9100 G_CALLBACK(gtk_sheet_entry_changed_handler));
9101
9102 _gtk_sheet_hide_active_cell(sheet);
9103
9104 sheet->active_cell.row = -1; /* reset before signal emission, to prevent recursion */
9105 sheet->active_cell.col = -1;
9106
9107 /* beware: DEACTIVATE handler may call gtk_sheet_set_active_cell()
9108 */
9109
9110 _gtkextra_signal_emit(GTK_OBJECT(sheet), sheet_signals[DEACTIVATE],
9111 row, col, &veto);
9112
9113 if (!veto)
9114 {
9115 sheet->active_cell.row = row;
9116 sheet->active_cell.col = col;
9117
9118 return (FALSE);
9119 }
9120
9121 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9122 g_debug("gtk_sheet_deactivate_cell: running");
9123 #endif
9124
9125
9126 if (GTK_SHEET_REDRAW_PENDING(sheet))
9127 {
9128 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_REDRAW_PENDING);
9129 _gtk_sheet_range_draw(sheet, NULL, TRUE);
9130 }
9131
9132 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9133 g_debug("gtk_sheet_deactivate_cell: done row %d col %d", row, col);
9134 #endif
9135
9136 return (TRUE);
9137 }
9138
9139 /**
9140 * _gtk_sheet_hide_active_cell:
9141 *
9142 * @param sheet the #GtkSheet
9143 *
9144 * hide active cell
9145 */
9146 void
_gtk_sheet_hide_active_cell(GtkSheet * sheet)9147 _gtk_sheet_hide_active_cell(GtkSheet *sheet)
9148 {
9149 gint row, col;
9150
9151 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
9152 return;
9153 if (!gtk_widget_get_visible(sheet->sheet_entry))
9154 return;
9155
9156 row = sheet->active_cell.row;
9157 col = sheet->active_cell.col;
9158
9159 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9160 g_debug("_gtk_sheet_hide_active_cell: called row %d col %d", row, col);
9161 #endif
9162
9163 if (row < 0 || row > sheet->maxrow)
9164 return;
9165 if (col < 0 || col > sheet->maxcol)
9166 return;
9167
9168 if (sheet->freeze_count == 0)
9169 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IS_FROZEN);
9170
9171 #if 0
9172 /* transferring entry text to the cell gives problems when gtk_sheet_change_entry()
9173 is called during TRAVERSE signal emission. The text of the already changed
9174 entry gets written into the deactivated cell */
9175 {
9176 char *text = gtk_sheet_get_entry_text(sheet);
9177
9178 /* todo: compare with existing text, only notify if text changes */
9179 gtk_sheet_set_cell_text(sheet, row, col, text);
9180
9181 g_free(text);
9182 }
9183 #endif
9184
9185 #if 0
9186 /* why shoud we first set the cursor to the cell we want hide ? */
9187 g_signal_emit(GTK_OBJECT(sheet),sheet_signals[SET_CELL], 0, row, col);
9188 #endif
9189
9190 if (!GTK_SHEET_IS_FROZEN(sheet))
9191 {
9192 GtkSheetRange range;
9193
9194 range.row0 = range.rowi = row;
9195 range.col0 = range.coli = col;
9196
9197 _gtk_sheet_range_draw(sheet, &range, FALSE); /* do not reactivate active cell!!! */
9198 }
9199
9200 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9201 g_debug("_gtk_sheet_hide_active_cell: _gtk_sheet_column_button_release");
9202 #endif
9203 _gtk_sheet_column_button_release(sheet, col);
9204 row_button_release(sheet, row);
9205
9206 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9207 g_debug("_gtk_sheet_hide_active_cell: gtk_widget_unmap");
9208 #endif
9209 gtk_widget_unmap(sheet->sheet_entry);
9210
9211 gdk_draw_pixmap(sheet->sheet_window,
9212 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
9213 sheet->pixmap,
9214 _gtk_sheet_column_left_xpixel(sheet, col) - 1,
9215 _gtk_sheet_row_top_ypixel(sheet, row) - 1,
9216 _gtk_sheet_column_left_xpixel(sheet, col) - 1,
9217 _gtk_sheet_row_top_ypixel(sheet, row) - 1,
9218 COLPTR(sheet, col)->width + 4,
9219 sheet->row[row].height + 4);
9220
9221 #if 0
9222 /* why shoud we first set the cursor to the cell we want hide ? */
9223 gtk_widget_grab_focus(GTK_WIDGET(sheet));
9224 #endif
9225
9226 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9227 g_debug("_gtk_sheet_hide_active_cell: gtk_widget_set_visible");
9228 #endif
9229
9230 gtk_widget_set_visible(GTK_WIDGET(sheet->sheet_entry), FALSE);
9231
9232 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9233 g_debug("_gtk_sheet_hide_active_cell: done");
9234 #endif
9235 }
9236
9237 static gboolean
gtk_sheet_activate_cell(GtkSheet * sheet,gint row,gint col)9238 gtk_sheet_activate_cell(GtkSheet *sheet, gint row, gint col)
9239 {
9240 gboolean veto = TRUE;
9241
9242 g_return_val_if_fail(sheet != NULL, FALSE);
9243 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
9244
9245 if (GTK_SHEET_FLAGS(sheet) & GTK_SHEET_IS_DESTROYED) return(FALSE); /* PR#102114 */
9246
9247 if (row < 0 || col < 0)
9248 return (FALSE);
9249 if (row > sheet->maxrow || col > sheet->maxcol)
9250 return (FALSE);
9251
9252 if (!gtk_widget_get_can_focus(GTK_WIDGET(sheet)))
9253 {
9254 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9255 g_debug("gtk_sheet_activate_cell: row %d col %d abort: sheet, can-focus false", row, col);
9256 #endif
9257 return (FALSE);
9258 }
9259
9260 if (!GTK_SHEET_COLUMN_CAN_FOCUS(COLPTR(sheet, col)))
9261 {
9262 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9263 g_debug("gtk_sheet_activate_cell: row %d col %d abort: sheet column, can-focus false", row, col);
9264 #endif
9265 return (FALSE);
9266 }
9267
9268 /*
9269 _gtkextra_signal_emit(GTK_OBJECT(sheet),sheet_signals[ACTIVATE], row, col, &veto);
9270 if(!gtk_widget_get_realized(GTK_WIDGET(sheet))) return veto;
9271 if (!veto) return FALSE;
9272 */
9273
9274 if (sheet->state != GTK_SHEET_NORMAL)
9275 {
9276 sheet->state = GTK_SHEET_NORMAL;
9277 gtk_sheet_real_unselect_range(sheet, NULL);
9278 }
9279
9280 sheet->range.row0 = row;
9281 sheet->range.col0 = col;
9282 sheet->range.rowi = row;
9283 sheet->range.coli = col;
9284
9285 sheet->active_cell.row = row;
9286 sheet->active_cell.col = col;
9287
9288 sheet->selection_cell.row = row;
9289 sheet->selection_cell.col = col;
9290
9291 row_button_set(sheet, row);
9292 _gtk_sheet_column_button_set(sheet, col);
9293
9294 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
9295 gtk_sheet_show_active_cell(sheet);
9296
9297 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9298 g_debug("gtk_sheet_activate_cell: signal setup");
9299 #endif
9300
9301 gtk_sheet_entry_signal_connect_changed(sheet,
9302 G_CALLBACK(gtk_sheet_entry_changed_handler));
9303
9304 _gtkextra_signal_emit(GTK_OBJECT(sheet), sheet_signals[ACTIVATE], row, col, &veto);
9305
9306 return (TRUE);
9307 }
9308
_gtk_sheet_entry_preselect(GtkSheet * sheet)9309 static void _gtk_sheet_entry_preselect(GtkSheet *sheet)
9310 {
9311 gboolean select_on_focus;
9312 gboolean editable = TRUE; /* incomplete - refer to gtkitementry::gtk_entry_grab_focus() */
9313 gboolean in_click = FALSE; /* incomplete - refer to gtkitementry::gtk_entry_grab_focus() */
9314
9315 #if GTK_SHEET_DEBUG_MOVE > 0
9316 g_debug("_gtk_sheet_entry_preselect: called");
9317 #endif
9318
9319 g_object_get(G_OBJECT(gtk_settings_get_default()),
9320 "gtk-entry-select-on-focus",
9321 &select_on_focus,
9322 NULL);
9323
9324 if (select_on_focus && editable && !in_click)
9325 {
9326 gtk_sheet_entry_select_region(sheet, 0, -1);
9327 }
9328 }
9329
9330 /**
9331 * _gtk_sheet_entry_setup:
9332 * @sheet: the #GtkSheet
9333 * @row: row index
9334 * @col: column index
9335 * @entry_widget: entry widget
9336 *
9337 * configure sheet_entry for use within the active cell
9338 * - justification
9339 * - editable
9340 * - max_length
9341 * - wrap_mode
9342 * - color attributes
9343 * - font
9344 *
9345 * this function must not dependant on any text contents.
9346 */
_gtk_sheet_entry_setup(GtkSheet * sheet,gint row,gint col,GtkWidget * entry_widget)9347 static void _gtk_sheet_entry_setup(GtkSheet *sheet, gint row, gint col,
9348 GtkWidget *entry_widget)
9349 {
9350 GtkJustification justification = GTK_JUSTIFY_LEFT;
9351 gboolean editable;
9352 GtkStyle *style;
9353 GtkSheetColumn *colptr = COLPTR(sheet, col);
9354 GtkSheetRow *rowptr = ROWPTR(sheet, row);
9355
9356 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9357 g_debug("_gtk_sheet_entry_setup: row %d col %d", row, col);
9358 #endif
9359
9360 if (row < 0 || col < 0)
9361 return;
9362
9363 GtkSheetCellAttr attributes;
9364 gtk_sheet_get_attributes(sheet, row, col, &attributes);
9365
9366 if (gtk_sheet_justify_entry(sheet))
9367 justification = attributes.justification;
9368
9369 editable = !(gtk_sheet_locked(sheet)
9370 || !attributes.is_editable
9371 || colptr->is_readonly
9372 || rowptr->is_readonly);
9373
9374 gtk_sheet_set_entry_editable(sheet, editable);
9375
9376 if (GTK_IS_ITEM_ENTRY(entry_widget))
9377 {
9378 GtkItemEntry *item_entry = GTK_ITEM_ENTRY(entry_widget);
9379 GtkEntry *entry = GTK_ENTRY(entry_widget);
9380
9381 /* 5.8.2010/fp - the code below has no effect in GTK 2.18.9,
9382 a right justified editable will pop to left justification
9383 as soon as something gets selected, and will
9384 pop back to right aligment, as soon as the
9385 cursor ist moved. When this happens, the
9386 justification value in the editable is correct. */
9387
9388 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9389 g_debug("_gtk_sheet_entry_setup: GtkItemEntry justification %d", justification);
9390 #endif
9391 gtk_item_entry_set_justification(item_entry, justification);
9392 gtk_item_entry_set_max_length_bytes(item_entry, colptr->max_length_bytes);
9393
9394 gtk_entry_set_max_length(entry, colptr->max_length);
9395 }
9396 else if (GTK_IS_DATA_TEXT_VIEW(entry_widget))
9397 {
9398 GtkDataTextView *data_textview = GTK_DATA_TEXT_VIEW(entry_widget);
9399 GtkTextView *textview = GTK_TEXT_VIEW(entry_widget);
9400
9401 gtk_text_view_set_wrap_mode(textview, colptr->wrap_mode);
9402 gtk_data_text_view_set_max_length(data_textview, colptr->max_length);
9403 gtk_data_text_view_set_max_length_bytes(data_textview, colptr->max_length_bytes);
9404 }
9405 else if (GTK_IS_TEXT_VIEW(entry_widget))
9406 {
9407 GtkTextView *textview = GTK_TEXT_VIEW(entry_widget);
9408
9409 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9410 g_debug("_gtk_sheet_entry_setup: GtkTextView justification %d", justification);
9411 #endif
9412 gtk_text_view_set_justification(textview, justification);
9413 gtk_text_view_set_wrap_mode(textview, colptr->wrap_mode);
9414 }
9415 else if (GTK_IS_ENTRY(entry_widget))
9416 {
9417 GtkEntry *entry = GTK_ENTRY(entry_widget);
9418 gtk_entry_set_max_length(entry, colptr->max_length);
9419 }
9420
9421 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9422 g_debug("_gtk_sheet_entry_setup: style bg color %s",
9423 gdk_color_to_string(&attributes.background));
9424 #endif
9425
9426 #if 1
9427 if (gtk_widget_get_realized(entry_widget))
9428 {
9429 style = gtk_widget_get_style(entry_widget);
9430 if (!style)
9431 gtk_widget_ensure_style(entry_widget); /* why this ?? */
9432 style = gtk_widget_get_style(entry_widget);
9433
9434 style = gtk_style_copy(style);
9435
9436 style->bg[GTK_STATE_NORMAL] = attributes.background;
9437 style->base[GTK_STATE_NORMAL] = attributes.background;
9438 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
9439 style->text[GTK_STATE_NORMAL] = attributes.foreground;
9440
9441 style->bg[GTK_STATE_ACTIVE] = attributes.background;
9442 style->base[GTK_STATE_ACTIVE] = attributes.background;
9443 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
9444 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
9445
9446 pango_font_description_free(style->font_desc);
9447 style->font_desc = pango_font_description_copy(attributes.font_desc);
9448
9449 gtk_widget_set_style(entry_widget, style);
9450
9451 g_object_unref(style); /* 22.06.13/fp */
9452 }
9453 #endif
9454 }
9455
9456 static void
gtk_sheet_show_active_cell(GtkSheet * sheet)9457 gtk_sheet_show_active_cell(GtkSheet *sheet)
9458 {
9459 gchar *text = NULL;
9460 gchar *old_text;
9461 gint row, col;
9462 gboolean is_visible = TRUE;
9463
9464 g_return_if_fail(sheet != NULL);
9465 g_return_if_fail(GTK_IS_SHEET(sheet));
9466
9467 row = sheet->active_cell.row;
9468 col = sheet->active_cell.col;
9469
9470 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9471 g_debug(
9472 "gtk_sheet_show_active_cell: called row %d col %d insel %d",
9473 row, col, GTK_SHEET_IN_SELECTION(sheet));
9474 #endif
9475
9476 if (row < 0 || col < 0)
9477 return;
9478 if (row > sheet->maxrow || col > sheet->maxcol)
9479 return;
9480
9481 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
9482 return;
9483 if (sheet->state != GTK_SHEET_NORMAL)
9484 return;
9485 if (GTK_SHEET_IN_SELECTION(sheet))
9486 return;
9487 if (!sheet->sheet_entry) /* PR#102114 */
9488 return;
9489
9490 /* we should send a ENTRY_CHANGE_REQUEST signal here */
9491
9492 if ((row <= sheet->maxallocrow) && (col <= sheet->maxalloccol)
9493 && sheet->data[row])
9494 {
9495 GtkSheetCell *cell = sheet->data[row][col];
9496 if (cell)
9497 {
9498 if (cell->text)
9499 text = g_strdup(cell->text);
9500 if (cell->attributes)
9501 is_visible = cell->attributes->is_visible;
9502 }
9503 }
9504
9505 GtkWidget *entry_widget = gtk_sheet_get_entry(sheet);
9506
9507 /* update visibility */
9508
9509 gtk_widget_set_visible(GTK_WIDGET(sheet->sheet_entry), is_visible);
9510
9511 if (GTK_IS_ENTRY(entry_widget))
9512 {
9513 gtk_entry_set_visibility(GTK_ENTRY(entry_widget), is_visible);
9514 }
9515
9516 /* update text */
9517
9518 if (!text)
9519 text = g_strdup("");
9520
9521 old_text = gtk_sheet_get_entry_text(sheet);
9522
9523 /* the entry setup must be done before the text assigment,
9524 otherwise max_length may cause text assignment to fail
9525 */
9526 _gtk_sheet_entry_setup(sheet, row, col, entry_widget);
9527
9528 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9529 g_debug("gtk_sheet_show_active_cell: old_text <%s> text <%s>", old_text, text);
9530 #endif
9531
9532 if (!old_text || (old_text[0] != text[0]) || strcmp(old_text, text) != 0)
9533 {
9534 gtk_sheet_set_entry_text(sheet, text);
9535 }
9536
9537 /* we should send an ENTRY_CONFIGURATION signal here */
9538
9539 gtk_sheet_entry_set_max_size(sheet);
9540 _gtk_sheet_entry_size_allocate(sheet);
9541
9542 gtk_widget_map(sheet->sheet_entry);
9543 gtk_sheet_draw_active_cell(sheet);
9544
9545 _gtk_sheet_entry_preselect(sheet);
9546
9547 gtk_widget_grab_focus(entry_widget);
9548
9549 g_free(text);
9550 g_free(old_text);
9551 }
9552
9553 static void
gtk_sheet_draw_active_cell(GtkSheet * sheet)9554 gtk_sheet_draw_active_cell(GtkSheet *sheet)
9555 {
9556 gint row, col;
9557
9558 if (!gtk_widget_is_drawable(GTK_WIDGET(sheet)))
9559 return;
9560 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
9561 return;
9562
9563 row = sheet->active_cell.row;
9564 col = sheet->active_cell.col;
9565
9566 if (row < 0 || row > sheet->maxrow)
9567 return;
9568 if (col < 0 || col > sheet->maxcol)
9569 return;
9570
9571 if (!gtk_sheet_cell_isvisible(sheet, row, col))
9572 return;
9573
9574 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9575 g_debug("gtk_sheet_draw_active_cell: row %d col %d", row, col);
9576 #endif
9577
9578 row_button_set(sheet, row);
9579 _gtk_sheet_column_button_set(sheet, col);
9580
9581 gtk_sheet_draw_backing_pixmap(sheet, sheet->range);
9582 gtk_sheet_draw_border(sheet, sheet->range);
9583 }
9584
9585
9586 static void
gtk_sheet_make_backing_pixmap(GtkSheet * sheet,guint width,guint height)9587 gtk_sheet_make_backing_pixmap(GtkSheet *sheet, guint width, guint height)
9588 {
9589 gint pixmap_width, pixmap_height;
9590
9591 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
9592 return;
9593
9594 if (width == 0 && height == 0)
9595 {
9596 width = sheet->sheet_window_width + 80;
9597 height = sheet->sheet_window_height + 80;
9598 }
9599
9600 if (!sheet->pixmap)
9601 {
9602 /* allocate */
9603 sheet->pixmap = gdk_pixmap_new(sheet->sheet_window,
9604 width, height,
9605 -1);
9606 if (!GTK_SHEET_IS_FROZEN(sheet))
9607 _gtk_sheet_range_draw(sheet, NULL, TRUE);
9608 }
9609 else
9610 {
9611 /* reallocate if sizes don't match */
9612 gdk_window_get_size(sheet->pixmap, &pixmap_width, &pixmap_height);
9613
9614 if ((pixmap_width != width) || (pixmap_height != height))
9615 {
9616 g_object_unref(G_OBJECT(sheet->pixmap));
9617 sheet->pixmap = gdk_pixmap_new(sheet->sheet_window,
9618 width, height,
9619 -1);
9620 if (!GTK_SHEET_IS_FROZEN(sheet))
9621 _gtk_sheet_range_draw(sheet, NULL, TRUE);
9622 }
9623 }
9624 }
9625
9626 static void
gtk_sheet_new_selection(GtkSheet * sheet,GtkSheetRange * range)9627 gtk_sheet_new_selection(GtkSheet *sheet, GtkSheetRange *range)
9628 {
9629 gint i, j, mask1, mask2;
9630 gint state, selected;
9631 gint x, y, width, height;
9632 GtkSheetRange new_range, aux_range;
9633
9634 g_return_if_fail(sheet != NULL);
9635
9636 if (range == NULL)
9637 range = &sheet->range;
9638
9639 new_range = *range;
9640
9641 range->row0 = MIN(range->row0, sheet->range.row0);
9642 range->rowi = MAX(range->rowi, sheet->range.rowi);
9643 range->col0 = MIN(range->col0, sheet->range.col0);
9644 range->coli = MAX(range->coli, sheet->range.coli);
9645
9646 range->row0 = MAX(range->row0, MIN_VIEW_ROW(sheet));
9647 range->rowi = MIN(range->rowi, MAX_VIEW_ROW(sheet));
9648 range->col0 = MAX(range->col0, MIN_VIEW_COLUMN(sheet));
9649 range->coli = MIN(range->coli, MAX_VIEW_COLUMN(sheet));
9650
9651 aux_range.row0 = MAX(new_range.row0, MIN_VIEW_ROW(sheet));
9652 aux_range.rowi = MIN(new_range.rowi, MAX_VIEW_ROW(sheet));
9653 aux_range.col0 = MAX(new_range.col0, MIN_VIEW_COLUMN(sheet));
9654 aux_range.coli = MIN(new_range.coli, MAX_VIEW_COLUMN(sheet));
9655
9656 for (i = range->row0; i <= range->rowi; i++)
9657 {
9658 if (i > sheet->maxrow)
9659 break;
9660
9661 for (j = range->col0; j <= range->coli; j++)
9662 {
9663 if (j > sheet->maxcol)
9664 break;
9665
9666 state = gtk_sheet_cell_get_state(sheet, i, j);
9667 selected = (i <= new_range.rowi && i >= new_range.row0 &&
9668 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
9669
9670 if (state == GTK_STATE_SELECTED && selected &&
9671 GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, j)) && GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)) &&
9672 (i == sheet->range.row0 || i == sheet->range.rowi ||
9673 j == sheet->range.col0 || j == sheet->range.coli ||
9674 i == new_range.row0 || i == new_range.rowi ||
9675 j == new_range.col0 || j == new_range.coli))
9676 {
9677
9678 mask1 = i == sheet->range.row0 ? 1 : 0;
9679 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
9680 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
9681 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
9682
9683 mask2 = i == new_range.row0 ? 1 : 0;
9684 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
9685 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
9686 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
9687
9688 if (mask1 != mask2)
9689 {
9690 x = _gtk_sheet_column_left_xpixel(sheet, j);
9691 y = _gtk_sheet_row_top_ypixel(sheet, i);
9692 width = _gtk_sheet_column_left_xpixel(sheet, j) - x + COLPTR(sheet, j)->width;
9693 height = _gtk_sheet_row_top_ypixel(sheet, i) - y + sheet->row[i].height;
9694
9695 if (i == sheet->range.row0)
9696 {
9697 y = y - 3;
9698 height = height + 3;
9699 }
9700 if (i == sheet->range.rowi)
9701 height = height + 3;
9702 if (j == sheet->range.col0)
9703 {
9704 x = x - 3;
9705 width = width + 3;
9706 }
9707 if (j == sheet->range.coli)
9708 width = width + 3;
9709
9710 gdk_draw_pixmap(sheet->sheet_window,
9711 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
9712 sheet->pixmap,
9713 x + 1,
9714 y + 1,
9715 x + 1,
9716 y + 1,
9717 width,
9718 height);
9719
9720 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
9721 {
9722 x = _gtk_sheet_column_left_xpixel(sheet, j);
9723 y = _gtk_sheet_row_top_ypixel(sheet, i);
9724 width = _gtk_sheet_column_left_xpixel(sheet, j) - x + COLPTR(sheet, j)->width;
9725 height = _gtk_sheet_row_top_ypixel(sheet, i) - y + sheet->row[i].height;
9726
9727 if (i == new_range.row0)
9728 {
9729 y = y + 2;
9730 height = height - 2;
9731 }
9732 if (i == new_range.rowi)
9733 height = height - 3;
9734 if (j == new_range.col0)
9735 {
9736 x = x + 2;
9737 width = width - 2;
9738 }
9739 if (j == new_range.coli)
9740 width = width - 3;
9741
9742 gdk_draw_rectangle(sheet->sheet_window,
9743 sheet->xor_gc,
9744 TRUE,
9745 x + 1, y + 1,
9746 width, height);
9747 }
9748 }
9749 }
9750 }
9751 }
9752
9753 for (i = range->row0; i <= range->rowi; i++)
9754 {
9755 for (j = range->col0; j <= range->coli; j++)
9756 {
9757 state = gtk_sheet_cell_get_state(sheet, i, j);
9758 selected = (i <= new_range.rowi && i >= new_range.row0 &&
9759 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
9760
9761 if (state == GTK_STATE_SELECTED && !selected &&
9762 GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, j)) &&
9763 GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
9764 {
9765 x = _gtk_sheet_column_left_xpixel(sheet, j);
9766 y = _gtk_sheet_row_top_ypixel(sheet, i);
9767 width = _gtk_sheet_column_left_xpixel(sheet, j) - x + COLPTR(sheet, j)->width;
9768 height = _gtk_sheet_row_top_ypixel(sheet, i) - y + sheet->row[i].height;
9769
9770 if (i == sheet->range.row0)
9771 {
9772 y = y - 3;
9773 height = height + 3;
9774 }
9775 if (i == sheet->range.rowi)
9776 height = height + 3;
9777 if (j == sheet->range.col0)
9778 {
9779 x = x - 3;
9780 width = width + 3;
9781 }
9782 if (j == sheet->range.coli)
9783 width = width + 3;
9784
9785 gdk_draw_pixmap(sheet->sheet_window,
9786 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
9787 sheet->pixmap,
9788 x + 1,
9789 y + 1,
9790 x + 1,
9791 y + 1,
9792 width,
9793 height);
9794 }
9795 }
9796 }
9797
9798 for (i = range->row0; i <= range->rowi; i++)
9799 {
9800 for (j = range->col0; j <= range->coli; j++)
9801 {
9802 state = gtk_sheet_cell_get_state(sheet, i, j);
9803 selected = (i <= new_range.rowi && i >= new_range.row0 &&
9804 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
9805
9806 if (state != GTK_STATE_SELECTED && selected &&
9807 GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, j)) && GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)) &&
9808 (i != sheet->active_cell.row || j != sheet->active_cell.col))
9809 {
9810 x = _gtk_sheet_column_left_xpixel(sheet, j);
9811 y = _gtk_sheet_row_top_ypixel(sheet, i);
9812 width = _gtk_sheet_column_left_xpixel(sheet, j) - x + COLPTR(sheet, j)->width;
9813 height = _gtk_sheet_row_top_ypixel(sheet, i) - y + sheet->row[i].height;
9814
9815 if (i == new_range.row0)
9816 {
9817 y = y + 2;
9818 height = height - 2;
9819 }
9820 if (i == new_range.rowi)
9821 height = height - 3;
9822 if (j == new_range.col0)
9823 {
9824 x = x + 2;
9825 width = width - 2;
9826 }
9827 if (j == new_range.coli)
9828 width = width - 3;
9829
9830 gdk_draw_rectangle(sheet->sheet_window,
9831 sheet->xor_gc,
9832 TRUE,
9833 x + 1, y + 1,
9834 width, height);
9835 }
9836 }
9837 }
9838
9839 for (i = aux_range.row0; i <= aux_range.rowi; i++)
9840 {
9841 for (j = aux_range.col0; j <= aux_range.coli; j++)
9842 {
9843 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, j)) && GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
9844 {
9845 state = gtk_sheet_cell_get_state(sheet, i, j);
9846
9847 mask1 = i == sheet->range.row0 ? 1 : 0;
9848 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
9849 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
9850 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
9851
9852 mask2 = i == new_range.row0 ? 1 : 0;
9853 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
9854 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
9855 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
9856 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
9857 {
9858 x = _gtk_sheet_column_left_xpixel(sheet, j);
9859 y = _gtk_sheet_row_top_ypixel(sheet, i);
9860 width = COLPTR(sheet, j)->width;
9861 height = sheet->row[i].height;
9862 if (mask2 & 1)
9863 gdk_draw_rectangle(sheet->sheet_window,
9864 sheet->xor_gc,
9865 TRUE,
9866 x + 1, y - 1,
9867 width, 3);
9868
9869 if (mask2 & 2)
9870 gdk_draw_rectangle(sheet->sheet_window,
9871 sheet->xor_gc,
9872 TRUE,
9873 x + 1, y + height - 1,
9874 width, 3);
9875
9876 if (mask2 & 4)
9877 gdk_draw_rectangle(sheet->sheet_window,
9878 sheet->xor_gc,
9879 TRUE,
9880 x - 1, y + 1,
9881 3, height);
9882
9883 if (mask2 & 8)
9884 gdk_draw_rectangle(sheet->sheet_window,
9885 sheet->xor_gc,
9886 TRUE,
9887 x + width - 1, y + 1,
9888 3, height);
9889 }
9890 }
9891 }
9892 }
9893
9894 *range = new_range;
9895 gtk_sheet_draw_corners(sheet, new_range);
9896 }
9897
9898 static void
gtk_sheet_draw_border(GtkSheet * sheet,GtkSheetRange new_range)9899 gtk_sheet_draw_border(GtkSheet *sheet, GtkSheetRange new_range)
9900 {
9901 GdkRectangle clip_area, area;
9902 gint i;
9903
9904 area.x = _gtk_sheet_column_left_xpixel(sheet, new_range.col0);
9905 area.y = _gtk_sheet_row_top_ypixel(sheet, new_range.row0);
9906 area.width = _gtk_sheet_column_right_xpixel(sheet, new_range.coli) - area.x;
9907 area.height = _gtk_sheet_row_bottom_ypixel(sheet, new_range.rowi) - area.y;
9908
9909 clip_area.x = sheet->row_title_area.width;
9910 clip_area.y = sheet->column_title_area.height;
9911 clip_area.width = sheet->sheet_window_width;
9912 clip_area.height = sheet->sheet_window_height;
9913
9914 if (!sheet->row_titles_visible)
9915 clip_area.x = 0;
9916 if (!sheet->column_titles_visible)
9917 clip_area.y = 0;
9918
9919 if (area.x < 0)
9920 {
9921 area.width = area.width + area.x;
9922 area.x = 0;
9923 }
9924 if (area.width > clip_area.width)
9925 area.width = clip_area.width + 10;
9926 if (area.y < 0)
9927 {
9928 area.height = area.height + area.y;
9929 area.y = 0;
9930 }
9931 if (area.height > clip_area.height)
9932 area.height = clip_area.height + 10;
9933
9934 #if GTK_SHEET_DEBUG_CELL_ACTIVATION > 0
9935 g_debug("gtk_sheet_draw_border: clip_area (%d, %d, %d, %d)",
9936 clip_area.x, clip_area.y, clip_area.width, clip_area.height);
9937 #endif
9938
9939 clip_area.x--;
9940 clip_area.y--;
9941 clip_area.width += 3;
9942 clip_area.height += 3;
9943
9944 gdk_gc_set_clip_rectangle(sheet->xor_gc, &clip_area);
9945
9946 for (i = -1; i <= 1; ++i)
9947 {
9948 gdk_draw_rectangle(sheet->sheet_window,
9949 sheet->xor_gc,
9950 FALSE,
9951 area.x + i, area.y + i,
9952 area.width - 2 * i, area.height - 2 * i);
9953 }
9954
9955 gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL);
9956 gtk_sheet_draw_corners(sheet, new_range);
9957 }
9958
9959 static void
gtk_sheet_draw_corners(GtkSheet * sheet,GtkSheetRange range)9960 gtk_sheet_draw_corners(GtkSheet *sheet, GtkSheetRange range)
9961 {
9962 gint x, y;
9963 guint width = 1;
9964
9965 if (gtk_sheet_cell_isvisible(sheet, range.row0, range.col0))
9966 {
9967 x = _gtk_sheet_column_left_xpixel(sheet, range.col0);
9968 y = _gtk_sheet_row_top_ypixel(sheet, range.row0);
9969 gdk_draw_pixmap(sheet->sheet_window,
9970 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
9971 sheet->pixmap,
9972 x - 1,
9973 y - 1,
9974 x - 1,
9975 y - 1,
9976 3,
9977 3);
9978 gdk_draw_rectangle(sheet->sheet_window,
9979 sheet->xor_gc,
9980 TRUE,
9981 x - 1, y - 1,
9982 3, 3);
9983 }
9984
9985 if (gtk_sheet_cell_isvisible(sheet, range.row0, range.coli) ||
9986 sheet->state == GTK_SHEET_COLUMN_SELECTED)
9987 {
9988 x = _gtk_sheet_column_left_xpixel(sheet, range.coli) +
9989 COLPTR(sheet, range.coli)->width;
9990 y = _gtk_sheet_row_top_ypixel(sheet, range.row0);
9991 width = 1;
9992 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
9993 {
9994 y = _gtk_sheet_row_top_ypixel(sheet, sheet->view.row0) + 3;
9995 width = 3;
9996 }
9997 gdk_draw_pixmap(sheet->sheet_window,
9998 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
9999 sheet->pixmap,
10000 x - width,
10001 y - width,
10002 x - width,
10003 y - width,
10004 2 * width + 1,
10005 2 * width + 1);
10006 gdk_draw_rectangle(sheet->sheet_window,
10007 sheet->xor_gc,
10008 TRUE,
10009 x - width + width / 2, y - width + width / 2,
10010 2 + width, 2 + width);
10011 }
10012
10013 if (gtk_sheet_cell_isvisible(sheet, range.rowi, range.col0) ||
10014 sheet->state == GTK_SHEET_ROW_SELECTED)
10015 {
10016 x = _gtk_sheet_column_left_xpixel(sheet, range.col0);
10017 y = _gtk_sheet_row_top_ypixel(sheet, range.rowi) +
10018 sheet->row[range.rowi].height;
10019 width = 1;
10020 if (sheet->state == GTK_SHEET_ROW_SELECTED)
10021 {
10022 x = _gtk_sheet_column_left_xpixel(sheet, sheet->view.col0) + 3;
10023 width = 3;
10024 }
10025 gdk_draw_pixmap(sheet->sheet_window,
10026 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
10027 sheet->pixmap,
10028 x - width,
10029 y - width,
10030 x - width,
10031 y - width,
10032 2 * width + 1,
10033 2 * width + 1);
10034 gdk_draw_rectangle(sheet->sheet_window,
10035 sheet->xor_gc,
10036 TRUE,
10037 x - width + width / 2, y - width + width / 2,
10038 2 + width, 2 + width);
10039 }
10040
10041 if (gtk_sheet_cell_isvisible(sheet, range.rowi, range.coli))
10042 {
10043 x = _gtk_sheet_column_left_xpixel(sheet, range.coli) +
10044 COLPTR(sheet, range.coli)->width;
10045 y = _gtk_sheet_row_top_ypixel(sheet, range.rowi) +
10046 sheet->row[range.rowi].height;
10047 width = 1;
10048 if (sheet->state == GTK_SHEET_RANGE_SELECTED)
10049 width = 3;
10050 if (sheet->state == GTK_SHEET_NORMAL)
10051 width = 3;
10052 gdk_draw_pixmap(sheet->sheet_window,
10053 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
10054 sheet->pixmap,
10055 x - width,
10056 y - width,
10057 x - width,
10058 y - width,
10059 2 * width + 1,
10060 2 * width + 1);
10061 gdk_draw_rectangle(sheet->sheet_window,
10062 sheet->xor_gc,
10063 TRUE,
10064 x - width + width / 2, y - width + width / 2,
10065 2 + width, 2 + width);
10066
10067 }
10068
10069 }
10070
10071
10072 static void
gtk_sheet_real_select_range(GtkSheet * sheet,GtkSheetRange * range)10073 gtk_sheet_real_select_range(GtkSheet *sheet, GtkSheetRange *range)
10074 {
10075 gint i;
10076 gint state;
10077
10078 g_return_if_fail(sheet != NULL);
10079
10080 if (range == NULL)
10081 range = &sheet->range;
10082
10083 if (range->row0 < 0 || range->rowi < 0)
10084 return;
10085 if (range->col0 < 0 || range->coli < 0)
10086 return;
10087
10088 #if GTK_SHEET_DEBUG_SELECTION > 0
10089 g_debug("gtk_sheet_real_select_range: {%d, %d, %d, %d}",
10090 range->row0, range->col0, range->rowi, range->coli);
10091 #endif
10092
10093 state = sheet->state;
10094
10095 if (state == GTK_SHEET_COLUMN_SELECTED || state == GTK_SHEET_RANGE_SELECTED)
10096 {
10097 for (i = sheet->range.col0; i < range->col0; i++) _gtk_sheet_column_button_release(sheet, i);
10098
10099 for (i = range->coli + 1; i <= sheet->range.coli; i++) _gtk_sheet_column_button_release(sheet, i);
10100
10101 for (i = range->col0; i <= range->coli; i++)
10102 {
10103 _gtk_sheet_column_button_set(sheet, i);
10104 }
10105 }
10106
10107 if (state == GTK_SHEET_ROW_SELECTED || state == GTK_SHEET_RANGE_SELECTED)
10108 {
10109 for (i = sheet->range.row0; i < range->row0; i++) row_button_release(sheet, i);
10110
10111 for (i = range->rowi + 1; i <= sheet->range.rowi; i++) row_button_release(sheet, i);
10112
10113 for (i = range->row0; i <= range->rowi; i++)
10114 {
10115 row_button_set(sheet, i);
10116 }
10117 }
10118
10119 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
10120 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
10121 {
10122 gtk_sheet_new_selection(sheet, range);
10123
10124 sheet->range.col0 = range->col0;
10125 sheet->range.coli = range->coli;
10126 sheet->range.row0 = range->row0;
10127 sheet->range.rowi = range->rowi;
10128 }
10129 else
10130 {
10131 gtk_sheet_draw_backing_pixmap(sheet, sheet->range);
10132 gtk_sheet_range_draw_selection(sheet, sheet->range);
10133 }
10134
10135 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[SELECT_RANGE], 0, range);
10136 }
10137
10138 /**
10139 * gtk_sheet_select_range:
10140 * @sheet: a #GtkSheet
10141 * @range: a #GtkSheetRange
10142 *
10143 * Highlight the selected range and store bounds in sheet->range
10144 */
10145 void
gtk_sheet_select_range(GtkSheet * sheet,const GtkSheetRange * range)10146 gtk_sheet_select_range(GtkSheet *sheet, const GtkSheetRange *range)
10147 {
10148 GtkSheetRange new_range; /* buffer needed because gtk_sheet_real_unselect_range() will clear it */
10149
10150 g_return_if_fail(sheet != NULL);
10151
10152 if (!range)
10153 range = &sheet->range;
10154
10155 new_range = *range;
10156
10157 if (new_range.row0 < 0 || new_range.rowi < 0)
10158 return;
10159 if (new_range.col0 < 0 || new_range.coli < 0)
10160 return;
10161
10162 if (sheet->state != GTK_SHEET_NORMAL)
10163 {
10164 /* this will clear sheet->range */
10165 gtk_sheet_real_unselect_range(sheet, NULL);
10166 }
10167 else
10168 {
10169 gboolean veto = TRUE;
10170 veto = gtk_sheet_deactivate_cell(sheet);
10171 if (!veto)
10172 return;
10173 }
10174
10175 sheet->range.row0 = new_range.row0;
10176 sheet->range.rowi = new_range.rowi;
10177 sheet->range.col0 = new_range.col0;
10178 sheet->range.coli = new_range.coli;
10179
10180 sheet->active_cell.row = new_range.row0;
10181 sheet->active_cell.col = new_range.col0;
10182
10183 sheet->selection_cell.row = new_range.rowi;
10184 sheet->selection_cell.col = new_range.coli;
10185
10186 sheet->state = GTK_SHEET_RANGE_SELECTED;
10187 gtk_sheet_real_select_range(sheet, NULL);
10188
10189 }
10190 /**
10191 * gtk_sheet_unselect_range:
10192 * @sheet: a #GtkSheet
10193 *
10194 * Unselect the current selected range and clears the bounds in sheet->range.
10195 */
10196 void
gtk_sheet_unselect_range(GtkSheet * sheet)10197 gtk_sheet_unselect_range(GtkSheet *sheet)
10198 {
10199 gtk_sheet_real_unselect_range(sheet, NULL);
10200 sheet->state = GTK_SHEET_NORMAL;
10201 gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col);
10202 }
10203
10204
10205 static void
gtk_sheet_real_unselect_range(GtkSheet * sheet,GtkSheetRange * range)10206 gtk_sheet_real_unselect_range(GtkSheet *sheet, GtkSheetRange *range)
10207 {
10208 gint i;
10209
10210 g_return_if_fail(sheet != NULL);
10211 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
10212 return;
10213
10214 if (!range)
10215 range = &sheet->range;
10216
10217 #if GTK_SHEET_DEBUG_SELECTION > 0
10218 g_debug("gtk_sheet_real_unselect_range: called {%d, %d, %d, %d}",
10219 range->row0, range->col0, range->rowi, range->coli);
10220 #endif
10221
10222 if (range->row0 < 0 || range->rowi < 0)
10223 return;
10224 if (range->col0 < 0 || range->coli < 0)
10225 return;
10226
10227 if (gtk_sheet_range_isvisible(sheet, *range))
10228 {
10229 #if GTK_SHEET_DEBUG_SELECTION > 0
10230 g_debug("gtk_sheet_real_unselect_range: gtk_sheet_draw_backing_pixmap");
10231 #endif
10232 gtk_sheet_draw_backing_pixmap(sheet, *range);
10233 }
10234
10235 for (i = range->col0; i <= range->coli; i++)
10236 {
10237 #if GTK_SHEET_DEBUG_SELECTION > 0
10238 g_debug("gtk_sheet_real_unselect_range: _gtk_sheet_column_button_release(%d)", i);
10239 #endif
10240 _gtk_sheet_column_button_release(sheet, i);
10241 }
10242
10243 for (i = range->row0; i <= range->rowi; i++)
10244 {
10245 #if GTK_SHEET_DEBUG_SELECTION > 0
10246 g_debug("gtk_sheet_real_unselect_range: row_button_release(%d)", i);
10247 #endif
10248 row_button_release(sheet, i);
10249 }
10250
10251 #if GTK_SHEET_DEBUG_SELECTION > 0
10252 g_debug("gtk_sheet_real_unselect_range: gtk_sheet_position_children()");
10253 #endif
10254 gtk_sheet_position_children(sheet);
10255
10256 /* reset range */
10257 range->row0 = range->rowi = range->col0 = range->coli = -1;
10258 }
10259
10260
10261 /*
10262 * gtk_sheet_expose_handler:
10263 *
10264 * this is the #GtkSheet widget class "expose-event" signal handler
10265 *
10266 * @param widget the #GtkSheet
10267 * @param event the GdkEventExpose which triggered this signal
10268 *
10269 * @return TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
10270 */
10271 static gboolean
gtk_sheet_expose_handler(GtkWidget * widget,GdkEventExpose * event)10272 gtk_sheet_expose_handler(GtkWidget *widget, GdkEventExpose *event)
10273 {
10274 GtkSheet *sheet;
10275 gint i;
10276
10277 g_return_val_if_fail(widget != NULL, FALSE);
10278 g_return_val_if_fail(GTK_IS_SHEET(widget), FALSE);
10279 g_return_val_if_fail(event != NULL, FALSE);
10280
10281 sheet = GTK_SHEET(widget);
10282
10283 if (gtk_widget_is_drawable(widget))
10284 {
10285 #if GTK_SHEET_DEBUG_EXPOSE > 0
10286 g_debug("gtk_sheet_expose_handler: called");
10287 #endif
10288
10289 if (event->window == sheet->row_title_window && sheet->row_titles_visible)
10290 {
10291 #if GTK_SHEET_DEBUG_EXPOSE > 0
10292 g_debug("gtk_sheet_expose_handler: row buttons");
10293 #endif
10294 for (i = MIN_VIEW_ROW(sheet); i <= MAX_VIEW_ROW(sheet) && i <= sheet->maxrow; i++)
10295 {
10296 _gtk_sheet_draw_button(sheet, i, -1);
10297 }
10298 }
10299
10300 if (event->window == sheet->column_title_window && sheet->column_titles_visible)
10301 {
10302 #if GTK_SHEET_DEBUG_EXPOSE > 0
10303 g_debug("gtk_sheet_expose_handler: column buttons");
10304 #endif
10305 for (i = MIN_VIEW_COLUMN(sheet); i <= MAX_VIEW_COLUMN(sheet) && i <= sheet->maxcol; i++)
10306 {
10307 _gtk_sheet_draw_button(sheet, -1, i);
10308 }
10309 }
10310
10311 if (event->window == sheet->sheet_window)
10312 {
10313 GtkSheetRange range;
10314
10315 range.row0 = _gtk_sheet_row_from_ypixel(sheet, event->area.y);
10316 range.col0 = _gtk_sheet_column_from_xpixel(sheet, event->area.x);
10317 range.rowi = _gtk_sheet_row_from_ypixel(sheet, event->area.y + event->area.height);
10318 range.coli = _gtk_sheet_column_from_xpixel(sheet, event->area.x + event->area.width);
10319
10320 #if GTK_SHEET_DEBUG_EXPOSE > 0
10321 g_debug("gtk_sheet_expose_handler: backing pixmap (%d,%d) (%d,%d)",
10322 range.row0, range.col0, range.rowi, range.coli);
10323 #endif
10324
10325 gtk_sheet_draw_backing_pixmap(sheet, range);
10326
10327 if (sheet->state != GTK_SHEET_NORMAL)
10328 {
10329 if (gtk_sheet_range_isvisible(sheet, sheet->range))
10330 gtk_sheet_draw_backing_pixmap(sheet, sheet->range);
10331 if (GTK_SHEET_IN_RESIZE(sheet) || GTK_SHEET_IN_DRAG(sheet))
10332 gtk_sheet_draw_backing_pixmap(sheet, sheet->drag_range);
10333
10334 if (gtk_sheet_range_isvisible(sheet, sheet->range))
10335 gtk_sheet_range_draw_selection(sheet, sheet->range);
10336 if (GTK_SHEET_IN_RESIZE(sheet) || GTK_SHEET_IN_DRAG(sheet))
10337 draw_xor_rectangle(sheet, sheet->drag_range);
10338 }
10339
10340 if ((!GTK_SHEET_IN_XDRAG(sheet)) &&
10341 (!GTK_SHEET_IN_YDRAG(sheet)))
10342 {
10343 if (sheet->state == GTK_SHEET_NORMAL)
10344 {
10345 #if GTK_SHEET_DEBUG_EXPOSE > 0
10346 g_debug(
10347 "gtk_sheet_expose_handler: draw_active_cell ar %d ac %d",
10348 sheet->active_cell.row,
10349 sheet->active_cell.col);
10350 #endif
10351
10352 gtk_sheet_draw_active_cell(sheet);
10353
10354 #if GTK_SHEET_DEBUG_EXPOSE > 0
10355 g_debug("gtk_sheet_expose_handler: insel %d",
10356 GTK_SHEET_IN_SELECTION(sheet));
10357 #endif
10358 if (!GTK_SHEET_IN_SELECTION(sheet)){
10359 gtk_widget_queue_draw(sheet->sheet_entry);
10360 }
10361 }
10362 }
10363 }
10364 }
10365
10366 if (sheet->state != GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION(sheet))
10367 gtk_widget_grab_focus(GTK_WIDGET(sheet));
10368
10369 (*GTK_WIDGET_CLASS(sheet_parent_class)->expose_event)(widget, event);
10370
10371 return (FALSE);
10372 }
10373
10374
10375 /*
10376 * gtk_sheet_button_press_handler:
10377 *
10378 * this is the #GtkSheet widget class "button-press-event" handler
10379 *
10380 * @param widget the #GtkSheet
10381 * @param event the GdkEventButton which triggered this signal
10382 *
10383 * @return TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
10384 */
10385 static gboolean
gtk_sheet_button_press_handler(GtkWidget * widget,GdkEventButton * event)10386 gtk_sheet_button_press_handler(GtkWidget *widget, GdkEventButton *event)
10387 {
10388 GtkSheet *sheet;
10389 GdkModifierType mods;
10390 gint x, y, row, column;
10391 gboolean veto;
10392
10393 g_return_val_if_fail(widget != NULL, FALSE);
10394 g_return_val_if_fail(GTK_IS_SHEET(widget), FALSE);
10395 g_return_val_if_fail(event != NULL, FALSE);
10396 /*
10397 if(event->type != GDK_BUTTON_PRESS) return(TRUE);
10398 */
10399
10400 #if GTK_SHEET_DEBUG_MOUSE > 0
10401 g_debug("gtk_sheet_button_press_handler: called");
10402 #endif
10403
10404 gdk_window_get_pointer(gtk_widget_get_window(widget),
10405 NULL, NULL, &mods);
10406
10407 if (!(mods & GDK_BUTTON1_MASK))
10408 return (TRUE);
10409
10410 sheet = GTK_SHEET(widget);
10411
10412 /* press on resize windows */
10413 if (event->window == sheet->column_title_window && gtk_sheet_columns_resizable(sheet))
10414 {
10415 gtk_widget_get_pointer(widget, &sheet->x_drag, NULL);
10416 if (POSSIBLE_XDRAG(sheet, sheet->x_drag, &sheet->drag_cell.col))
10417 {
10418 guint req;
10419 if (event->type == GDK_2BUTTON_PRESS)
10420 {
10421 _gtk_sheet_autoresize_column_internal(sheet, sheet->drag_cell.col);
10422 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_XDRAG);
10423 return (TRUE);
10424 }
10425 _gtk_sheet_column_size_request(sheet, sheet->drag_cell.col, &req);
10426 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_XDRAG);
10427 gdk_pointer_grab(sheet->column_title_window, FALSE,
10428 GDK_POINTER_MOTION_HINT_MASK |
10429 GDK_BUTTON1_MOTION_MASK |
10430 GDK_BUTTON_RELEASE_MASK,
10431 NULL, NULL, event->time);
10432
10433 draw_xor_vline(sheet);
10434 return (TRUE);
10435 }
10436 }
10437
10438 if (event->window == sheet->row_title_window && gtk_sheet_rows_resizable(sheet))
10439 {
10440 gtk_widget_get_pointer(widget, NULL, &sheet->y_drag);
10441
10442 if (POSSIBLE_YDRAG(sheet, sheet->y_drag, &sheet->drag_cell.row))
10443 {
10444 guint req;
10445 gtk_sheet_row_size_request(sheet, sheet->drag_cell.row, &req);
10446 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_YDRAG);
10447 gdk_pointer_grab(sheet->row_title_window, FALSE,
10448 GDK_POINTER_MOTION_HINT_MASK |
10449 GDK_BUTTON1_MOTION_MASK |
10450 GDK_BUTTON_RELEASE_MASK,
10451 NULL, NULL, event->time);
10452
10453 draw_xor_hline(sheet);
10454 return (TRUE);
10455 }
10456 }
10457
10458 /* the sheet itself does not handle other than single click events */
10459 if (event->type != GDK_BUTTON_PRESS)
10460 return (FALSE);
10461
10462 /* selections on the sheet */
10463 if (event->window == sheet->sheet_window)
10464 {
10465 #if GTK_SHEET_DEBUG_MOUSE > 0
10466 g_debug("gtk_sheet_button_press_handler: on sheet");
10467 #endif
10468
10469 gtk_widget_get_pointer(widget, &x, &y);
10470 gtk_sheet_get_pixel_info(sheet, NULL, x, y, &row, &column);
10471 if (row < 0 && column < 0) return(FALSE); /* chain up to global button press handler*/
10472
10473 #if GTK_SHEET_DEBUG_MOUSE > 0
10474 g_debug("gtk_sheet_button_press_handler: pointer grab (%d,%d) r %d c %d mr %d mc %d",
10475 x, y, row, column, sheet->maxrow, sheet->maxcol
10476 );
10477 #endif
10478 gdk_pointer_grab(sheet->sheet_window, FALSE,
10479 GDK_POINTER_MOTION_HINT_MASK |
10480 GDK_BUTTON1_MOTION_MASK |
10481 GDK_BUTTON_RELEASE_MASK,
10482 NULL, NULL, event->time);
10483 gtk_grab_add(GTK_WIDGET(sheet));
10484 sheet->timer = g_timeout_add_full(0, TIMEOUT_SCROLL, _gtk_sheet_scroll_to_pointer, sheet, NULL);
10485 #if GTK_SHEET_DEBUG_MOUSE > 0
10486 g_debug("gtk_sheet_button_press_handler: grab focus");
10487 #endif
10488 gtk_widget_grab_focus(GTK_WIDGET(sheet));
10489
10490 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
10491 gdk_cursor_get_cursor_type(sheet->cursor_drag) == GDK_SIZING &&
10492 !GTK_SHEET_IN_SELECTION(sheet) && !GTK_SHEET_IN_RESIZE(sheet))
10493 {
10494 if (sheet->state == GTK_STATE_NORMAL)
10495 {
10496 gint row = sheet->active_cell.row; /* PR#203012 */
10497 gint column = sheet->active_cell.col; /* PR#203012 */
10498
10499 if (!gtk_sheet_deactivate_cell(sheet))
10500 return (FALSE);
10501 sheet->active_cell.row = row;
10502 sheet->active_cell.col = column;
10503 sheet->drag_range = sheet->range;
10504 sheet->state = GTK_SHEET_RANGE_SELECTED;
10505 gtk_sheet_select_range(sheet, &sheet->drag_range);
10506 }
10507 sheet->x_drag = x;
10508 sheet->y_drag = y;
10509 if (row > sheet->range.rowi)
10510 row--;
10511 if (column > sheet->range.coli)
10512 column--;
10513 sheet->drag_cell.row = row;
10514 sheet->drag_cell.col = column;
10515 sheet->drag_range = sheet->range;
10516 draw_xor_rectangle(sheet, sheet->drag_range);
10517 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_RESIZE);
10518 }
10519 else if (gdk_cursor_get_cursor_type(sheet->cursor_drag) == GDK_TOP_LEFT_ARROW &&
10520 !GTK_SHEET_IN_SELECTION(sheet) && !GTK_SHEET_IN_DRAG(sheet))
10521 {
10522 if (sheet->state == GTK_STATE_NORMAL)
10523 {
10524 gint row = sheet->active_cell.row; /* PR#203012 */
10525 gint column = sheet->active_cell.col; /* PR#203012 */
10526
10527 if (!gtk_sheet_deactivate_cell(sheet))
10528 return (FALSE);
10529 sheet->active_cell.row = row;
10530 sheet->active_cell.col = column;
10531 sheet->drag_range = sheet->range;
10532 sheet->state = GTK_SHEET_RANGE_SELECTED;
10533 gtk_sheet_select_range(sheet, &sheet->drag_range);
10534 }
10535 sheet->x_drag = x;
10536 sheet->y_drag = y;
10537 if (row < sheet->range.row0)
10538 row++;
10539 if (row > sheet->range.rowi)
10540 row--;
10541 if (column < sheet->range.col0)
10542 column++;
10543 if (column > sheet->range.coli)
10544 column--;
10545 sheet->drag_cell.row = row;
10546 sheet->drag_cell.col = column;
10547 sheet->drag_range = sheet->range;
10548 #if GTK_SHEET_DEBUG_MOUSE > 0
10549 g_debug("gtk_sheet_button_press_handler: drag_range r %d c %d (%d,%d, %d, %d) mr %d mc %d",
10550 sheet->drag_cell.row, sheet->drag_cell.col,
10551 sheet->drag_range.row0, sheet->drag_range.rowi,
10552 sheet->drag_range.col0, sheet->drag_range.coli,
10553 sheet->maxrow, sheet->maxcol
10554 );
10555 #endif
10556 draw_xor_rectangle(sheet, sheet->drag_range);
10557 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_DRAG);
10558 }
10559 else
10560 {
10561 #if GTK_SHEET_DEBUG_MOUSE > 0
10562 g_debug("gtk_sheet_button_press_handler: on click cell");
10563 #endif
10564
10565 gtk_sheet_click_cell(sheet, row, column, &veto);
10566 if (veto)
10567 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10568 }
10569 return(TRUE);
10570 }
10571
10572 if (event->window == sheet->column_title_window)
10573 {
10574 gtk_widget_get_pointer(widget, &x, &y);
10575 column = _gtk_sheet_column_from_xpixel(sheet, x);
10576 if (column < 0 || column > sheet->maxcol)
10577 return (FALSE);
10578
10579 if (GTK_SHEET_COLUMN_IS_SENSITIVE(COLPTR(sheet, column)))
10580 {
10581 gtk_sheet_click_cell(sheet, -1, column, &veto);
10582 gtk_grab_add(GTK_WIDGET(sheet));
10583 sheet->timer = g_timeout_add_full(0, TIMEOUT_SCROLL, _gtk_sheet_scroll_to_pointer, sheet, NULL);
10584 gtk_widget_grab_focus(GTK_WIDGET(sheet));
10585 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10586 }
10587 }
10588
10589 if (event->window == sheet->row_title_window)
10590 {
10591 gtk_widget_get_pointer(widget, &x, &y);
10592 row = _gtk_sheet_row_from_ypixel(sheet, y);
10593 if (row < 0 || row > sheet->maxrow)
10594 return (FALSE);
10595
10596 if (GTK_SHEET_ROW_IS_SENSITIVE(ROWPTR(sheet, row)))
10597 {
10598 gtk_sheet_click_cell(sheet, row, -1, &veto);
10599 gtk_grab_add(GTK_WIDGET(sheet));
10600 sheet->timer = g_timeout_add_full(0, TIMEOUT_SCROLL, _gtk_sheet_scroll_to_pointer, sheet, NULL);
10601 gtk_widget_grab_focus(GTK_WIDGET(sheet));
10602 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10603 }
10604 }
10605
10606 return (TRUE);
10607 }
10608
10609 static gint
_gtk_sheet_scroll_to_pointer(gpointer data)10610 _gtk_sheet_scroll_to_pointer(gpointer data)
10611 {
10612 GtkSheet *sheet = GTK_SHEET(data);
10613 gint x, y, row, column;
10614 gint move = TRUE;
10615
10616 GDK_THREADS_ENTER();
10617
10618 gtk_widget_get_pointer(GTK_WIDGET(sheet), &x, &y);
10619 gtk_sheet_get_pixel_info(sheet, NULL, x, y, &row, &column);
10620
10621 if (GTK_SHEET_IN_SELECTION(sheet))
10622 {
10623 GtkSheetRange visr;
10624
10625 /* beware of invisible columns/rows */
10626 if (!_gtk_sheet_get_visible_range(sheet, &visr))
10627 return (TRUE);
10628
10629 if (_POINT_IN_RANGE(row, column, &visr))
10630 {
10631 gtk_sheet_extend_selection(sheet, row, column);
10632 }
10633 }
10634
10635 if (GTK_SHEET_IN_DRAG(sheet) || GTK_SHEET_IN_RESIZE(sheet))
10636 {
10637 move = _gtk_sheet_move_query(sheet, row, column, FALSE);
10638 if (move)
10639 draw_xor_rectangle(sheet, sheet->drag_range);
10640 }
10641
10642 GDK_THREADS_LEAVE();
10643 return (TRUE);
10644 }
10645
10646 static void
gtk_sheet_click_cell(GtkSheet * sheet,gint row,gint col,gboolean * veto)10647 gtk_sheet_click_cell(GtkSheet *sheet, gint row, gint col, gboolean *veto)
10648 {
10649 *veto = TRUE;
10650
10651 #if GTK_SHEET_DEBUG_CLICK > 0
10652 g_debug("gtk_sheet_click_cell: called, row %d col %d", row, col);
10653 #endif
10654
10655 /* allow row,col < 0 here, see below */
10656 if (row > sheet->maxrow || col > sheet->maxcol)
10657 {
10658 *veto = FALSE;
10659 return;
10660 }
10661
10662 if (col >= 0 && row >= 0)
10663 {
10664 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) ||
10665 !GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)) ||
10666 !GTK_SHEET_CELL_IS_VISIBLE(sheet,row,col) ||
10667 !GTK_SHEET_COLUMN_IS_SENSITIVE(COLPTR(sheet,col))||
10668 !GTK_SHEET_ROW_IS_SENSITIVE(ROWPTR(sheet,row)) ||
10669 !GTK_SHEET_CELL_IS_SENSITIVE(sheet,row,col) ||
10670 !GTK_SHEET_COLUMN_CAN_FOCUS(COLPTR(sheet,col)) ||
10671 !GTK_SHEET_ROW_CAN_FOCUS(ROWPTR(sheet,row)) ||
10672 !GTK_SHEET_CELL_CAN_FOCUS(sheet,row,col) )
10673 {
10674 *veto = FALSE;
10675 return;
10676 }
10677 }
10678
10679
10680 /* if we do not grab focus here, some entry widgets (i.e. GtkSpinButton)
10681 will not format contents correctly on field exit */
10682
10683 gtk_widget_grab_focus(GTK_WIDGET(sheet));
10684
10685 _gtkextra_signal_emit(GTK_OBJECT(sheet), sheet_signals[TRAVERSE],
10686 sheet->active_cell.row, sheet->active_cell.col,
10687 &row, &col, veto);
10688
10689 if (!*veto)
10690 {
10691 if (sheet->state == GTK_STATE_NORMAL)
10692 return;
10693
10694 row = sheet->active_cell.row;
10695 col = sheet->active_cell.col;
10696 gtk_sheet_activate_cell(sheet, row, col);
10697 return;
10698 }
10699
10700 if (row == -1 && col >= 0) /* column button clicked */
10701 {
10702 if (gtk_sheet_autoscroll(sheet))
10703 _gtk_sheet_move_query(sheet, row, col, FALSE);
10704
10705 gtk_sheet_select_column(sheet, col);
10706 return;
10707 }
10708 if (col == -1 && row >= 0) /* row button clicked */
10709 {
10710 if (gtk_sheet_autoscroll(sheet))
10711 _gtk_sheet_move_query(sheet, row, col, FALSE);
10712
10713 gtk_sheet_select_row(sheet, row);
10714 return;
10715 }
10716
10717 if (row == -1 && col == -1) /* global button clicked */
10718 {
10719 sheet->range.row0 = 0;
10720 sheet->range.col0 = 0;
10721 sheet->range.rowi = sheet->maxrow;
10722 sheet->range.coli = sheet->maxcol;
10723
10724 sheet->active_cell.row = 0;
10725 sheet->active_cell.col = 0;
10726
10727 if (sheet->state != GTK_STATE_NORMAL) /* if any range is selected, clear it */
10728 gtk_sheet_unselect_range(sheet);
10729 else
10730 gtk_sheet_select_range(sheet, NULL);
10731
10732 return;
10733 }
10734
10735 if (row != -1 && col != -1) /* sheet cell clicked */
10736 {
10737 GtkSheetColumn *colp = COLPTR(sheet, col);
10738
10739 if (!gtk_widget_get_can_focus(GTK_WIDGET(sheet)))
10740 {
10741 #if GTK_SHEET_DEBUG_CLICK > 0
10742 g_debug("gtk_sheet_click_cell: row %d col %d VETO: sheet, can-focus false", row, col);
10743 #endif
10744 *veto = FALSE;
10745 return;
10746 }
10747
10748 if (!GTK_SHEET_COLUMN_CAN_FOCUS(colp))
10749 {
10750 #if GTK_SHEET_DEBUG_CLICK > 0
10751 g_debug("gtk_sheet_click_cell: row %d col %d VETO: sheet column, can-focus false", row, col);
10752 #endif
10753 *veto = FALSE;
10754 return;
10755 }
10756
10757 if (sheet->state != GTK_SHEET_NORMAL)
10758 {
10759 sheet->state = GTK_SHEET_NORMAL;
10760 gtk_sheet_real_unselect_range(sheet, NULL);
10761 }
10762 else
10763 {
10764 #if GTK_SHEET_DEBUG_CLICK > 0
10765 g_debug("gtk_sheet_click_cell: row %d col %d calling deactivate", row, col);
10766 #endif
10767 if (!gtk_sheet_deactivate_cell(sheet))
10768 {
10769 #if GTK_SHEET_DEBUG_CLICK > 0
10770 g_debug("gtk_sheet_click_cell: row %d col %d VETO: deactivate false", row, col);
10771 #endif
10772 *veto = FALSE;
10773 return;
10774 }
10775 #if GTK_SHEET_DEBUG_CLICK > 0
10776 g_debug("gtk_sheet_click_cell: row %d col %d back from deactivate", row, col);
10777 #endif
10778 }
10779
10780 /* auto switch column entry_type */
10781 /* not sure wether to move this code to gtk_sheet_show_active_cell() */
10782 {
10783 GType installed_entry_type = sheet->installed_entry_type;
10784 GType wanted_type =
10785 (colp->entry_type != G_TYPE_NONE) ? colp->entry_type : sheet->entry_type;
10786
10787 if (installed_entry_type != wanted_type)
10788 {
10789 if (sheet->state == GTK_SHEET_NORMAL)
10790 _gtk_sheet_hide_active_cell(sheet);
10791
10792 create_sheet_entry(sheet, wanted_type ? wanted_type : G_TYPE_NONE);
10793 }
10794 }
10795
10796 /* DEACTIVATE handler might have called gtk_sheet_set_active_cell(),
10797 so wie leave it, if it was changed
10798 */
10799 #if 0
10800 {
10801 gint act_row = sheet->active_cell.row;
10802 gint act_col = sheet->active_cell.col;
10803
10804 if (act_row != -1 && act_col != -1 &&
10805 (sheet->active_cell.row != row || sheet->active_cell.col != col))
10806 {
10807 row = sheet->active_cell.row;
10808 col = sheet->active_cell.col;
10809 }
10810 }
10811 #endif
10812
10813 if (gtk_sheet_autoscroll(sheet))
10814 _gtk_sheet_move_query(sheet, row, col, TRUE);
10815
10816 sheet->active_cell.row = row;
10817 sheet->active_cell.col = col;
10818
10819 sheet->selection_cell.row = row;
10820 sheet->selection_cell.col = col;
10821
10822 sheet->range.row0 = row;
10823 sheet->range.col0 = col;
10824 sheet->range.rowi = row;
10825 sheet->range.coli = col;
10826
10827 sheet->state = GTK_SHEET_NORMAL;
10828
10829 GTK_SHEET_SET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10830 gtk_sheet_draw_active_cell(sheet);
10831 return;
10832 }
10833
10834 g_assert_not_reached();
10835 gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col);
10836 }
10837
10838 /*
10839 * gtk_sheet_button_release_handler:
10840 *
10841 * this is the #GtkSheet widget class "button-release-event" handler
10842 *
10843 * @param widget the #GtkSheet
10844 * @param event the GdkEventButton which triggered this signal
10845 *
10846 * @return TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
10847 */
10848 static gboolean
gtk_sheet_button_release_handler(GtkWidget * widget,GdkEventButton * event)10849 gtk_sheet_button_release_handler(GtkWidget *widget, GdkEventButton *event)
10850 {
10851 GtkSheet *sheet;
10852 gint x, y;
10853
10854 sheet = GTK_SHEET(widget);
10855
10856 /* release on resize windows */
10857 if (GTK_SHEET_IN_XDRAG(sheet))
10858 {
10859 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_XDRAG);
10860 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10861 gtk_widget_get_pointer(widget, &x, NULL);
10862 gdk_pointer_ungrab(event->time);
10863 draw_xor_vline(sheet);
10864
10865 #if GTK_SHEET_DEBUG_SIZE > 0
10866 g_debug("gtk_sheet_button_release_handler[%d]: set width %d",
10867 sheet->drag_cell.col, new_column_width(sheet, sheet->drag_cell.col, &x));
10868 #endif
10869 gtk_sheet_set_column_width(sheet,
10870 sheet->drag_cell.col, new_column_width(sheet, sheet->drag_cell.col, &x));
10871
10872 sheet->old_hadjustment = -1.;
10873
10874 if (sheet->hadjustment)
10875 {
10876 g_signal_emit_by_name(GTK_OBJECT(sheet->hadjustment), "value_changed");
10877 }
10878 return (TRUE);
10879 }
10880
10881 if (GTK_SHEET_IN_YDRAG(sheet))
10882 {
10883 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_YDRAG);
10884 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10885 gtk_widget_get_pointer(widget, NULL, &y);
10886 gdk_pointer_ungrab(event->time);
10887 draw_xor_hline(sheet);
10888
10889 gtk_sheet_set_row_height(sheet, sheet->drag_cell.row,
10890 new_row_height(sheet, sheet->drag_cell.row, &y));
10891
10892 sheet->old_vadjustment = -1.;
10893 if (sheet->vadjustment)
10894 {
10895 g_signal_emit_by_name(GTK_OBJECT(sheet->vadjustment), "value_changed");
10896 }
10897 return (TRUE);
10898 }
10899
10900
10901 if (GTK_SHEET_IN_DRAG(sheet))
10902 {
10903 GtkSheetRange old_range;
10904 draw_xor_rectangle(sheet, sheet->drag_range);
10905 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_DRAG);
10906 gdk_pointer_ungrab(event->time);
10907
10908 gtk_sheet_real_unselect_range(sheet, NULL);
10909
10910 sheet->active_cell.row = sheet->active_cell.row +
10911 (sheet->drag_range.row0 - sheet->range.row0);
10912 sheet->active_cell.col = sheet->active_cell.col +
10913 (sheet->drag_range.col0 - sheet->range.col0);
10914 sheet->selection_cell.row = sheet->selection_cell.row +
10915 (sheet->drag_range.row0 - sheet->range.row0);
10916 sheet->selection_cell.col = sheet->selection_cell.col +
10917 (sheet->drag_range.col0 - sheet->range.col0);
10918 old_range = sheet->range;
10919 sheet->range = sheet->drag_range;
10920 sheet->drag_range = old_range;
10921 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[MOVE_RANGE], 0,
10922 &sheet->drag_range, &sheet->range);
10923 gtk_sheet_select_range(sheet, &sheet->range);
10924 }
10925
10926 if (GTK_SHEET_IN_RESIZE(sheet))
10927 {
10928 GtkSheetRange old_range;
10929 draw_xor_rectangle(sheet, sheet->drag_range);
10930 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_RESIZE);
10931 gdk_pointer_ungrab(event->time);
10932
10933 gtk_sheet_real_unselect_range(sheet, NULL);
10934
10935 sheet->active_cell.row = sheet->active_cell.row +
10936 (sheet->drag_range.row0 - sheet->range.row0);
10937 sheet->active_cell.col = sheet->active_cell.col +
10938 (sheet->drag_range.col0 - sheet->range.col0);
10939 if (sheet->drag_range.row0 < sheet->range.row0)
10940 sheet->selection_cell.row = sheet->drag_range.row0;
10941 if (sheet->drag_range.rowi >= sheet->range.rowi)
10942 sheet->selection_cell.row = sheet->drag_range.rowi;
10943 if (sheet->drag_range.col0 < sheet->range.col0)
10944 sheet->selection_cell.col = sheet->drag_range.col0;
10945 if (sheet->drag_range.coli >= sheet->range.coli)
10946 sheet->selection_cell.col = sheet->drag_range.coli;
10947 old_range = sheet->range;
10948 sheet->range = sheet->drag_range;
10949 sheet->drag_range = old_range;
10950
10951 if (sheet->state == GTK_STATE_NORMAL)
10952 sheet->state = GTK_SHEET_RANGE_SELECTED;
10953 g_signal_emit(GTK_OBJECT(sheet), sheet_signals[RESIZE_RANGE], 0,
10954 &sheet->drag_range, &sheet->range);
10955 gtk_sheet_select_range(sheet, &sheet->range);
10956 }
10957
10958 if (sheet->state == GTK_SHEET_NORMAL && GTK_SHEET_IN_SELECTION(sheet))
10959 {
10960 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10961 gdk_pointer_ungrab(event->time);
10962 gtk_sheet_activate_cell(sheet, sheet->active_cell.row,
10963 sheet->active_cell.col);
10964 }
10965
10966 if (GTK_SHEET_IN_SELECTION)
10967 gdk_pointer_ungrab(event->time);
10968 if (sheet->timer)
10969 g_source_remove(sheet->timer);
10970 gtk_grab_remove(GTK_WIDGET(sheet));
10971
10972 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
10973
10974 return (TRUE);
10975 }
10976
10977 /*
10978 * gtk_sheet_motion_handler:<p>
10979 * this is the #GtkSheet widget class "motion-notify-event" signal handler
10980 *
10981 * @param widget the #GtkSheet
10982 * @param event the GdkEventMotion which triggered this signal
10983 *
10984 * @return TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
10985 */
10986 static gboolean
gtk_sheet_motion_handler(GtkWidget * widget,GdkEventMotion * event)10987 gtk_sheet_motion_handler(GtkWidget *widget, GdkEventMotion *event)
10988 {
10989 GtkSheet *sheet;
10990 GdkModifierType mods;
10991 GdkCursorType new_cursor;
10992 gint x, y, row, column;
10993
10994 g_return_val_if_fail(widget != NULL, FALSE);
10995 g_return_val_if_fail(GTK_IS_SHEET(widget), FALSE);
10996 g_return_val_if_fail(event != NULL, FALSE);
10997
10998
10999 sheet = GTK_SHEET(widget);
11000
11001 /* selections on the sheet */
11002 x = event->x;
11003 y = event->y;
11004
11005 #if GTK_SHEET_DEBUG_MOTION > 0
11006 g_debug("gtk_sheet_motion_handler: (%d,%d) inSel %s", x, y,
11007 GTK_SHEET_IN_SELECTION(sheet) ? "Yes" : "No" );
11008 #endif
11009
11010 if (event->window == sheet->column_title_window
11011 && gtk_sheet_columns_resizable(sheet))
11012 {
11013 gtk_widget_get_pointer(widget, &x, &y);
11014 if (!GTK_SHEET_IN_SELECTION(sheet) && POSSIBLE_XDRAG(sheet, x, &column))
11015 {
11016 new_cursor = GDK_SB_H_DOUBLE_ARROW;
11017 if (new_cursor != gdk_cursor_get_cursor_type(sheet->cursor_drag))
11018 {
11019 gdk_cursor_destroy(sheet->cursor_drag);
11020 sheet->cursor_drag = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
11021 gdk_window_set_cursor(sheet->column_title_window, sheet->cursor_drag);
11022 }
11023 }
11024 else
11025 {
11026 new_cursor = GDK_TOP_LEFT_ARROW;
11027 if (!GTK_SHEET_IN_XDRAG(sheet) && new_cursor != gdk_cursor_get_cursor_type(sheet->cursor_drag))
11028 {
11029 gdk_cursor_destroy(sheet->cursor_drag);
11030 sheet->cursor_drag = gdk_cursor_new(GDK_TOP_LEFT_ARROW);
11031 gdk_window_set_cursor(sheet->column_title_window, sheet->cursor_drag);
11032 }
11033 }
11034 }
11035
11036 if (event->window == sheet->row_title_window
11037 && gtk_sheet_rows_resizable(sheet))
11038 {
11039 gtk_widget_get_pointer(widget, &x, &y);
11040 if (!GTK_SHEET_IN_SELECTION(sheet) && POSSIBLE_YDRAG(sheet, y, &column))
11041 {
11042 new_cursor = GDK_SB_V_DOUBLE_ARROW;
11043 if (new_cursor != gdk_cursor_get_cursor_type(sheet->cursor_drag))
11044 {
11045 gdk_cursor_destroy(sheet->cursor_drag);
11046 sheet->cursor_drag = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
11047 gdk_window_set_cursor(sheet->row_title_window, sheet->cursor_drag);
11048 }
11049 }
11050 else
11051 {
11052 new_cursor = GDK_TOP_LEFT_ARROW;
11053 if (!GTK_SHEET_IN_YDRAG(sheet) && new_cursor != gdk_cursor_get_cursor_type(sheet->cursor_drag))
11054 {
11055 gdk_cursor_destroy(sheet->cursor_drag);
11056 sheet->cursor_drag = gdk_cursor_new(GDK_TOP_LEFT_ARROW);
11057 gdk_window_set_cursor(sheet->row_title_window, sheet->cursor_drag);
11058 }
11059 }
11060 }
11061
11062 new_cursor = GDK_PLUS;
11063 if (!POSSIBLE_DRAG(sheet, x, y, &row, &column) && !GTK_SHEET_IN_DRAG(sheet) &&
11064 !POSSIBLE_RESIZE(sheet, x, y, &row, &column) && !GTK_SHEET_IN_RESIZE(sheet) &&
11065 event->window == sheet->sheet_window &&
11066 new_cursor != gdk_cursor_get_cursor_type(sheet->cursor_drag))
11067 {
11068 gdk_cursor_destroy(sheet->cursor_drag);
11069 sheet->cursor_drag = gdk_cursor_new(GDK_PLUS);
11070 gdk_window_set_cursor(sheet->sheet_window, sheet->cursor_drag);
11071 }
11072
11073 new_cursor = GDK_TOP_LEFT_ARROW;
11074 if (!(POSSIBLE_RESIZE(sheet, x, y, &row, &column) || GTK_SHEET_IN_RESIZE(sheet)) &&
11075 (POSSIBLE_DRAG(sheet, x, y, &row, &column) || GTK_SHEET_IN_DRAG(sheet)) &&
11076 event->window == sheet->sheet_window &&
11077 new_cursor != gdk_cursor_get_cursor_type(sheet->cursor_drag))
11078 {
11079 gdk_cursor_destroy(sheet->cursor_drag);
11080 sheet->cursor_drag = gdk_cursor_new(GDK_TOP_LEFT_ARROW);
11081 gdk_window_set_cursor(sheet->sheet_window, sheet->cursor_drag);
11082 }
11083
11084 new_cursor = GDK_SIZING;
11085 if (!GTK_SHEET_IN_DRAG(sheet) &&
11086 (POSSIBLE_RESIZE(sheet, x, y, &row, &column) || GTK_SHEET_IN_RESIZE(sheet)) &&
11087 event->window == sheet->sheet_window &&
11088 new_cursor != gdk_cursor_get_cursor_type(sheet->cursor_drag))
11089 {
11090 gdk_cursor_destroy(sheet->cursor_drag);
11091 sheet->cursor_drag = gdk_cursor_new(GDK_SIZING);
11092 gdk_window_set_cursor(sheet->sheet_window, sheet->cursor_drag);
11093 }
11094
11095 gdk_window_get_pointer(gtk_widget_get_window(widget), &x, &y, &mods);
11096 if (!(mods & GDK_BUTTON1_MASK))
11097 return (FALSE);
11098
11099 if (GTK_SHEET_IN_XDRAG(sheet))
11100 {
11101 if (event->is_hint ||
11102 event->window != gtk_widget_get_window(widget))
11103 {
11104 gtk_widget_get_pointer(widget, &x, NULL);
11105 }
11106 else
11107 x = event->x;
11108
11109 new_column_width(sheet, sheet->drag_cell.col, &x);
11110 if (x != sheet->x_drag)
11111 {
11112 draw_xor_vline(sheet);
11113 sheet->x_drag = x;
11114 draw_xor_vline(sheet);
11115 }
11116 return (TRUE);
11117 }
11118
11119 if (GTK_SHEET_IN_YDRAG(sheet))
11120 {
11121 if (event->is_hint ||
11122 event->window != gtk_widget_get_window(widget))
11123 {
11124 gtk_widget_get_pointer(widget, NULL, &y);
11125 }
11126 else
11127 y = event->y;
11128
11129 new_row_height(sheet, sheet->drag_cell.row, &y);
11130 if (y != sheet->y_drag)
11131 {
11132 draw_xor_hline(sheet);
11133 sheet->y_drag = y;
11134 draw_xor_hline(sheet);
11135 }
11136 return (TRUE);
11137 }
11138
11139 if (GTK_SHEET_IN_DRAG(sheet))
11140 {
11141 GtkSheetRange aux, visr;
11142
11143 gint current_row = MIN(sheet->maxrow, _gtk_sheet_row_from_ypixel(sheet, y));
11144 gint current_col = MIN(sheet->maxcol, _gtk_sheet_column_from_xpixel(sheet, x));
11145
11146 row = current_row - sheet->drag_cell.row;
11147 column = current_col - sheet->drag_cell.col;
11148
11149 if (sheet->state == GTK_SHEET_ROW_SELECTED)
11150 column = 0;
11151 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
11152 row = 0;
11153 sheet->x_drag = x;
11154 sheet->y_drag = y;
11155 aux = sheet->range;
11156
11157 /* beware of invisible columns/rows */
11158 if (!_gtk_sheet_get_visible_range(sheet, &visr))
11159 return (TRUE);
11160
11161 if (_RECT_IN_RANGE(aux.row0 + row, aux.rowi + row,
11162 aux.col0 + column, aux.coli + column, &visr))
11163 {
11164 aux = sheet->drag_range;
11165
11166 #if 1
11167 /* The following code doesn't behave properly when there
11168 are invisible columns in the sheet. For a proper user experience
11169 it should
11170 1. _gtk_sheet_count_visible(drag_range)
11171 2. try to move the outer edge into the given direction
11172 3. find and add enough visible columns backwards
11173 Beware: above algo will modify width/height of the drag area
11174 */
11175 if (row > 0)
11176 {
11177 gint nrow0 = _gtk_sheet_first_visible_rowidx(sheet, sheet->range.row0 + row);
11178 gint nrowi = _gtk_sheet_first_visible_rowidx(sheet, sheet->range.rowi + row);
11179 if (nrow0 >= 0 && nrowi >= 0)
11180 {
11181 sheet->drag_range.row0 = nrow0;
11182 sheet->drag_range.rowi = nrowi;
11183 }
11184 }
11185 else
11186 {
11187 gint nrow0 = _gtk_sheet_last_visible_rowidx(sheet, sheet->range.row0 + row);
11188 gint nrowi = _gtk_sheet_last_visible_rowidx(sheet, sheet->range.rowi + row);
11189 if (nrow0 >= 0 && nrowi >= 0)
11190 {
11191 sheet->drag_range.row0 = nrow0;
11192 sheet->drag_range.rowi = nrowi;
11193 }
11194 }
11195 if (column > 0)
11196 {
11197 gint ncol0 = _gtk_sheet_first_visible_colidx(sheet, sheet->range.col0 + column);
11198 gint ncoli = _gtk_sheet_first_visible_colidx(sheet, sheet->range.coli + column);
11199 if (ncol0 >= 0 && ncoli >= 0)
11200 {
11201 sheet->drag_range.col0 = ncol0;
11202 sheet->drag_range.coli = ncoli;
11203 }
11204 }
11205 else
11206 {
11207 gint ncol0 = _gtk_sheet_last_visible_colidx(sheet, sheet->range.col0 + column);
11208 gint ncoli = _gtk_sheet_last_visible_colidx(sheet, sheet->range.coli + column);
11209 if (ncol0 >= 0 && ncoli >= 0)
11210 {
11211 sheet->drag_range.col0 = ncol0;
11212 sheet->drag_range.coli = ncoli;
11213 }
11214 }
11215 #endif
11216
11217 if (aux.row0 != sheet->drag_range.row0 ||
11218 aux.col0 != sheet->drag_range.col0)
11219 {
11220 draw_xor_rectangle(sheet, aux);
11221 draw_xor_rectangle(sheet, sheet->drag_range);
11222 }
11223 }
11224 return (TRUE);
11225 }
11226
11227 if (GTK_SHEET_IN_RESIZE(sheet))
11228 {
11229 GtkSheetRange aux, visr;
11230 gint current_col, current_row, col_threshold, row_threshold;
11231
11232 g_assert(0 <= sheet->drag_cell.row && sheet->drag_cell.row <= sheet->maxrow);
11233 g_assert(0 <= sheet->drag_cell.col && sheet->drag_cell.col <= sheet->maxcol);
11234
11235 current_row = MIN(sheet->maxrow, _gtk_sheet_row_from_ypixel(sheet, y));
11236 current_col = MIN(sheet->maxcol, _gtk_sheet_column_from_xpixel(sheet, x));
11237
11238 row = current_row - sheet->drag_cell.row;
11239 column = current_col - sheet->drag_cell.col;
11240
11241 #if GTK_SHEET_DEBUG_SELECTION > 0
11242 g_debug("gtk_sheet_motion: RESIZE row %d col %d cr %d cc %d",
11243 row, column, current_row, current_col);
11244 #endif
11245
11246 /*use half of column width resp. row height as threshold to expand selection*/
11247 row_threshold = _gtk_sheet_row_top_ypixel(sheet, current_row);
11248 if (current_row >= 0)
11249 row_threshold += (ROWPTR(sheet, current_row)->height) / 2;
11250
11251 if (current_row > sheet->drag_range.row0 && y < row_threshold)
11252 current_row = _gtk_sheet_last_visible_rowidx(sheet, current_row - 1);
11253 else if (current_row < sheet->drag_range.row0 && y < row_threshold)
11254 current_row = _gtk_sheet_first_visible_rowidx(sheet, current_row + 1);
11255
11256 col_threshold = _gtk_sheet_column_left_xpixel(sheet, current_col);
11257 if (current_col >= 0)
11258 col_threshold += (COLPTR(sheet, current_col)->width) / 2;
11259
11260 if (current_col > sheet->drag_range.col0 && x < col_threshold)
11261 current_col = _gtk_sheet_last_visible_colidx(sheet, current_col - 1);
11262 else if (current_col < sheet->drag_range.col0 && x > col_threshold)
11263 current_col = _gtk_sheet_first_visible_colidx(sheet, current_col + 1);
11264
11265 sheet->x_drag = x;
11266 sheet->y_drag = y;
11267 aux = sheet->range;
11268
11269 /* beware of invisible columns/rows */
11270 if (!_gtk_sheet_get_visible_range(sheet, &visr))
11271 return (TRUE);
11272
11273 #if GTK_SHEET_DEBUG_SELECTION > 0
11274 g_debug("gtk_sheet_motion: RESIZE th %d row %d col %d cr %d cc %d",
11275 row_threshold, row, column, current_row, current_col);
11276 #endif
11277
11278 if (_POINT_IN_RANGE(current_row, current_col, &visr))
11279 {
11280 aux = sheet->drag_range;
11281 sheet->drag_range = sheet->range;
11282
11283 if (sheet->state != GTK_SHEET_COLUMN_SELECTED)
11284 {
11285 if (current_row >= sheet->drag_range.row0)
11286 {
11287 sheet->drag_range.rowi = current_row;
11288 }
11289 else if (current_row < sheet->drag_range.row0)
11290 {
11291 sheet->drag_range.rowi = sheet->drag_range.row0;
11292 sheet->drag_range.row0 = current_row;
11293 }
11294 }
11295
11296 if (sheet->state != GTK_SHEET_ROW_SELECTED)
11297 {
11298 if (current_col >= sheet->drag_range.col0)
11299 {
11300 sheet->drag_range.coli = current_col;
11301 }
11302 else if (current_col < sheet->drag_range.col0)
11303 {
11304 sheet->drag_range.coli = sheet->drag_range.col0;
11305 sheet->drag_range.col0 = current_col;
11306 }
11307 }
11308
11309 if (_RANGE_NEQ_RANGE(&aux, &sheet->drag_range)
11310 {
11311 draw_xor_rectangle(sheet, aux);
11312 draw_xor_rectangle(sheet, sheet->drag_range);
11313 }
11314 }
11315 return (TRUE);
11316 }
11317
11318 gtk_sheet_get_pixel_info(sheet, NULL, x, y, &row, &column);
11319
11320 if (sheet->state == GTK_SHEET_NORMAL
11321 && row == sheet->active_cell.row && column == sheet->active_cell.col)
11322 {
11323 return (TRUE);
11324 }
11325
11326 if (GTK_SHEET_IN_SELECTION(sheet) && (mods & GDK_BUTTON1_MASK))
11327 {
11328 GtkSheetRange visr;
11329
11330 if (!_gtk_sheet_get_visible_range(sheet, &visr))
11331 return (TRUE);
11332
11333 if (_POINT_IN_RANGE(
11334 row >= 0 ? row : _gtk_sheet_first_visible_rowidx(sheet, 0),
11335 column >= 0 ? column : _gtk_sheet_first_visible_colidx(sheet, 0),
11336 &visr))
11337 {
11338 gtk_sheet_extend_selection(sheet, row, column);
11339 }
11340 }
11341
11342 return (TRUE);
11343 }
11344
11345 /* _HUNT_() statement macros find visible row/col into hunting direction */
11346
11347 #define _HUNT_VISIBLE_LEFT(col) \
11348 while (col > 0 \
11349 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col))) ) \
11350 col--; \
11351 if (col < 0) col = 0; \
11352 while (col < sheet->maxcol \
11353 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col))) ) \
11354 col++; \
11355 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) ) \
11356 col = -1;
11357
11358 #define _HUNT_VISIBLE_RIGHT(col) \
11359 while (col < sheet->maxcol \
11360 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col))) ) \
11361 col++; \
11362 if (col > sheet->maxcol) col = sheet->maxcol; \
11363 while (col > 0 \
11364 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col))) ) \
11365 col--; \
11366 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) ) \
11367 col = -1;
11368
11369 #define _HUNT_VISIBLE_UP(row) \
11370 while (row > 0 \
11371 && !GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row))) row--; \
11372 if (row < 0) row = 0; \
11373 while (row < sheet->maxrow \
11374 && !GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row))) row++; \
11375 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row))) row = -1;
11376
11377 #define _HUNT_VISIBLE_DOWN(row) \
11378 while (row < sheet->maxrow \
11379 && ((row < 0) || !GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row))) ) \
11380 row++; \
11381 if (row > sheet->maxrow) row = sheet->maxrow; \
11382 while (row > 0 \
11383 && !GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row))) row--; \
11384 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row))) row = -1;
11385
11386 #define _HUNT_FOCUS_LEFT(row,col) \
11387 while (col > 0 \
11388 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) \
11389 || !GTK_SHEET_COLUMN_CAN_GRAB_FOCUS(COLPTR(sheet,col)) \
11390 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col))) \
11391 col--; \
11392 if (col < 0) col = 0; \
11393 while (col < sheet->maxcol \
11394 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) \
11395 || !GTK_SHEET_COLUMN_CAN_GRAB_FOCUS(COLPTR(sheet, col)) \
11396 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col)) ) \
11397 col++; \
11398 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) \
11399 || !GTK_SHEET_COLUMN_CAN_GRAB_FOCUS(COLPTR(sheet, col)) \
11400 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col) ) \
11401 col = -1;
11402
11403 #define _HUNT_FOCUS_RIGHT(row,col) \
11404 while (col < sheet->maxcol \
11405 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) \
11406 || !GTK_SHEET_COLUMN_CAN_GRAB_FOCUS(COLPTR(sheet, col)) \
11407 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col)) ) \
11408 col++; \
11409 if (col > sheet->maxcol) col = sheet->maxcol; \
11410 while (col > 0 \
11411 && (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) \
11412 || !GTK_SHEET_COLUMN_CAN_GRAB_FOCUS(COLPTR(sheet, col)) \
11413 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col)) ) \
11414 col--; \
11415 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)) \
11416 || !GTK_SHEET_COLUMN_CAN_GRAB_FOCUS(COLPTR(sheet, col)) \
11417 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col) ) \
11418 col = -1;
11419
11420 #define _HUNT_FOCUS_UP(row,col) \
11421 while (row > 0 \
11422 && (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)) \
11423 || !GTK_SHEET_ROW_CAN_GRAB_FOCUS(ROWPTR(sheet, row)) \
11424 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col))) \
11425 row--; \
11426 if (row < 0) row = 0; \
11427 while (row < sheet->maxrow \
11428 && (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)) \
11429 || !GTK_SHEET_ROW_CAN_GRAB_FOCUS(ROWPTR(sheet, row)) \
11430 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col))) \
11431 row++; \
11432 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)) \
11433 || !GTK_SHEET_ROW_CAN_GRAB_FOCUS(ROWPTR(sheet, row)) \
11434 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col)) \
11435 row = -1;
11436
11437 #define _HUNT_FOCUS_DOWN(row,col) \
11438 while (row < sheet->maxrow \
11439 && (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)) \
11440 || !GTK_SHEET_ROW_CAN_GRAB_FOCUS(ROWPTR(sheet, row)) \
11441 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col))) \
11442 row++; \
11443 if (row > sheet->maxrow) row = sheet->maxrow; \
11444 while (row > 0 \
11445 && (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)) \
11446 || !GTK_SHEET_ROW_CAN_GRAB_FOCUS(ROWPTR(sheet, row)) \
11447 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col))) \
11448 row--; \
11449 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)) \
11450 || !GTK_SHEET_ROW_CAN_GRAB_FOCUS(ROWPTR(sheet, row)) \
11451 || !GTK_SHEET_CELL_TRAVERSABLE(sheet,row,col)) \
11452 row = -1;
11453
11454 static gint
11455 _gtk_sheet_move_query(GtkSheet *sheet, gint row, gint column,
11456 gboolean need_focus)
11457 {
11458 gint row_move = FALSE, column_move = FALSE;
11459 gint row_align = -1, col_align = -1; /* undef. */
11460 guint height, width;
11461 gint new_row = row;
11462 gint new_col = column;
11463 GtkSheetRange visr;
11464
11465 #if GTK_SHEET_DEBUG_MOVE > 0
11466 g_debug("gtk_sheet_move_query: row %d column %d view row %d-%d col %d-%d",
11467 row, column,
11468 MIN_VIEW_ROW(sheet), MAX_VIEW_ROW(sheet),
11469 MIN_VIEW_COLUMN(sheet), MAX_VIEW_COLUMN(sheet));
11470 #endif
11471
11472 height = sheet->sheet_window_height;
11473 width = sheet->sheet_window_width;
11474
11475 /* beware of invisible columns/rows */
11476 if (!_gtk_sheet_get_visible_range(sheet, &visr))
11477 return (0);
11478
11479 #if GTK_SHEET_DEBUG_MOVE > 0
11480 g_debug("gtk_sheet_move_query: state %d visr row %d-%d col %d-%d",
11481 sheet->state, visr.row0, visr.rowi, visr.col0, visr.coli);
11482 #endif
11483
11484 if (row >= MAX_VIEW_ROW(sheet)
11485 && row <= visr.rowi
11486 && sheet->state != GTK_SHEET_COLUMN_SELECTED)
11487 {
11488 #if GTK_SHEET_DEBUG_MOVE > 0
11489 g_debug("gtk_sheet_move_query: row %d > maxvr %d visr.rowi %d",
11490 row, MAX_VIEW_ROW(sheet), visr.rowi);
11491 #endif
11492 row_align = 1; /* bottom */
11493 if (need_focus)
11494 {
11495 _HUNT_FOCUS_DOWN(new_row,new_col);
11496 }
11497 else
11498 {
11499 _HUNT_VISIBLE_DOWN(new_row);
11500 }
11501 if (new_row >= 0) row_move = TRUE;
11502
11503 if (MAX_VIEW_ROW(sheet) == sheet->maxrow &&
11504 _gtk_sheet_row_bottom_ypixel(sheet, sheet->maxrow) < height)
11505 {
11506 row_move = FALSE;
11507 row_align = -1;
11508 }
11509 }
11510
11511 if (row <= MIN_VIEW_ROW(sheet)
11512 && row >= visr.row0
11513 && sheet->state != GTK_SHEET_COLUMN_SELECTED)
11514 {
11515 #if GTK_SHEET_DEBUG_MOVE > 0
11516 g_debug("gtk_sheet_move_query: row %d < minvr %d visr.row0 %d",
11517 row, MIN_VIEW_ROW(sheet), visr.row0);
11518 #endif
11519 row_align = 0; /* top */
11520 if (need_focus)
11521 {
11522 _HUNT_FOCUS_UP(new_row,new_col);
11523 }
11524 else
11525 {
11526 _HUNT_VISIBLE_UP(new_row);
11527 }
11528 if (new_row >= 0) row_move = TRUE;
11529 }
11530
11531 if (column >= MAX_VIEW_COLUMN(sheet)
11532 && column <= visr.coli
11533 && sheet->state != GTK_SHEET_ROW_SELECTED)
11534 {
11535 #if GTK_SHEET_DEBUG_MOVE > 0
11536 g_debug("gtk_sheet_move_query: col %d > maxvc %d visr.coli %d",
11537 column, MAX_VIEW_COLUMN(sheet), visr.coli);
11538 #endif
11539 col_align = 1; /* right */
11540 if (need_focus)
11541 {
11542 _HUNT_FOCUS_RIGHT(new_row,new_col);
11543 }
11544 else
11545 {
11546 _HUNT_VISIBLE_RIGHT(new_col);
11547 }
11548 if (new_col >= 0) column_move = TRUE;
11549
11550 if (MAX_VIEW_COLUMN(sheet) == sheet->maxcol &&
11551 _gtk_sheet_column_right_xpixel(sheet, sheet->maxcol) < width)
11552 {
11553 column_move = FALSE;
11554 col_align = -1;
11555 }
11556 }
11557
11558 if (column <= MIN_VIEW_COLUMN(sheet)
11559 && column >= visr.col0
11560 && sheet->state != GTK_SHEET_ROW_SELECTED)
11561 {
11562 #if GTK_SHEET_DEBUG_MOVE > 0
11563 g_debug("gtk_sheet_move_query: col %d < minvc %d visr.col0 %d",
11564 column, MIN_VIEW_COLUMN(sheet), visr.col0);
11565 #endif
11566 col_align = 0; /* left */
11567 if (need_focus)
11568 {
11569 _HUNT_FOCUS_LEFT(new_row,new_col);
11570 }
11571 else
11572 {
11573 _HUNT_VISIBLE_LEFT(new_col);
11574 }
11575 if (new_col >= 0) column_move = TRUE;
11576 }
11577
11578 #if GTK_SHEET_DEBUG_MOVE > 0
11579 g_debug("gtk_sheet_move_query: rowMv %d colMv %d newR %d newC %d",
11580 row_move, column_move, new_row, new_col);
11581 #endif
11582
11583 if (row_move || column_move)
11584 {
11585 gtk_sheet_moveto(sheet, new_row, new_col, row_align, col_align);
11586 }
11587
11588 return (row_move || column_move);
11589 }
11590
11591 static void
11592 gtk_sheet_extend_selection(GtkSheet *sheet, gint row, gint column)
11593 {
11594 GtkSheetRange range;
11595 gint state;
11596 gint r, c;
11597
11598 #if GTK_SHEET_DEBUG_SELECTION > 0
11599 g_debug("gtk_sheet_extend_selection: row %d column %d", row, column);
11600 #endif
11601
11602 if (sheet->selection_mode == GTK_SELECTION_SINGLE)
11603 return;
11604 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
11605 return;
11606
11607 if (sheet->active_cell.row < 0 || sheet->active_cell.row > sheet->maxrow)
11608 return;
11609 if (sheet->active_cell.col < 0 || sheet->active_cell.col > sheet->maxcol)
11610 return;
11611
11612 _gtk_sheet_move_query(sheet, row, column, FALSE);
11613 gtk_widget_grab_focus(GTK_WIDGET(sheet));
11614
11615 if (GTK_SHEET_IN_DRAG(sheet))
11616 return;
11617
11618 state = sheet->state;
11619
11620 switch(sheet->state)
11621 {
11622 case GTK_SHEET_ROW_SELECTED:
11623 column = sheet->maxcol;
11624 break;
11625
11626 case GTK_SHEET_COLUMN_SELECTED:
11627 row = sheet->maxrow;
11628 break;
11629
11630 case GTK_SHEET_NORMAL:
11631 r = sheet->active_cell.row;
11632 c = sheet->active_cell.col;
11633
11634 sheet->range.col0 = c;
11635 sheet->range.row0 = r;
11636 sheet->range.coli = c;
11637 sheet->range.rowi = r;
11638
11639 gdk_draw_pixmap(sheet->sheet_window,
11640 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[GTK_STATE_NORMAL],
11641 sheet->pixmap,
11642 _gtk_sheet_column_left_xpixel(sheet, c) - 1,
11643 _gtk_sheet_row_top_ypixel(sheet, r) - 1,
11644 _gtk_sheet_column_left_xpixel(sheet, c) - 1,
11645 _gtk_sheet_row_top_ypixel(sheet, r) - 1,
11646 COLPTR(sheet, c)->width + 4,
11647 sheet->row[r].height + 4);
11648
11649 sheet->state = GTK_SHEET_RANGE_SELECTED;
11650 gtk_sheet_range_draw_selection(sheet, sheet->range);
11651 /* FALLTHROUGH */
11652
11653 case GTK_SHEET_RANGE_SELECTED:
11654 sheet->state = GTK_SHEET_RANGE_SELECTED;
11655 /* FALLTHROUGH */
11656 }
11657
11658 sheet->selection_cell.row = row;
11659 sheet->selection_cell.col = column;
11660
11661 range.col0 = MIN(column, sheet->active_cell.col);
11662 range.coli = MAX(column, sheet->active_cell.col);
11663
11664 range.row0 = MIN(row, sheet->active_cell.row);
11665 range.rowi = MAX(row, sheet->active_cell.row);
11666
11667 range.coli = MIN(range.coli, sheet->maxcol);
11668 range.rowi = MIN(range.rowi, sheet->maxrow);
11669
11670 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
11671 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
11672 state == GTK_SHEET_NORMAL)
11673 {
11674 gtk_sheet_real_select_range(sheet, &range);
11675 }
11676 }
11677
11678 /*
11679 * gtk_sheet_entry_key_press_handler:
11680 *
11681 * this event handler propagates the "key-press-event" signal
11682 * from the sheet_entry to the #GtkSheet
11683 *
11684 * @param widget the #GtkSheet (connected "swapped")
11685 * @param key the GdkEventKey which triggered this signal
11686 *
11687 * @return TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
11688 */
11689 static gboolean
11690 gtk_sheet_entry_key_press_handler(GtkWidget *widget, GdkEventKey *key, gpointer user_data)
11691 {
11692 gboolean stop_emission = FALSE;
11693 GtkSheet *sheet = GTK_SHEET(widget);
11694
11695 #if GTK_SHEET_DEBUG_KEYPRESS > 0
11696 g_debug("gtk_sheet_entry_key_press_handler: called, key %s",
11697 gtk_accelerator_name(key->keyval, key->state));
11698 #endif
11699
11700 if (!_gtk_sheet_binding_filter(sheet, key))
11701 {
11702 #if GTK_SHEET_DEBUG_KEYPRESS > 0
11703 g_debug("gtk_sheet_entry_key_press_handler: filtered binding");
11704 #endif
11705 return (FALSE);
11706 }
11707
11708 #if 1
11709 /* process enter-event
11710 - detect wether Return or Enter was pressed
11711 - detect wether the application wants it to be handled by the sheet
11712 - if unwanted: execute appropriate application handler
11713 - use a new signal for this ?
11714 */
11715 if ((key->keyval == GDK_KEY_Return) || (key->keyval == GDK_KEY_KP_Enter))
11716 {
11717 #if GTK_SHEET_DEBUG_ENTER_PRESSED > 0
11718 g_debug("gtk_sheet_entry_key_press_handler: enter-pressed: invoke");
11719 #endif
11720 _gtkextra_signal_emit(GTK_OBJECT(sheet),
11721 sheet_signals[ENTER_PRESSED],
11722 key,
11723 &stop_emission);
11724 #if GTK_SHEET_DEBUG_ENTER_PRESSED > 0
11725 g_debug("gtk_sheet_entry_key_press_handler: enter-pressed: returns %d", stop_emission);
11726 #endif
11727 }
11728 #endif
11729
11730 if (!stop_emission)
11731 {
11732 /* intercept item_entry processing if there exists a key binding on the sheet */
11733
11734 if (gtk_bindings_activate_event(GTK_OBJECT(sheet), key))
11735 {
11736 stop_emission = TRUE;
11737 }
11738 else
11739 {
11740 g_signal_emit_by_name(GTK_OBJECT(widget),
11741 "key_press_event", key,
11742 &stop_emission);
11743 }
11744 }
11745
11746 #if GTK_SHEET_DEBUG_KEYPRESS > 0
11747 g_debug("gtk_sheet_entry_key_press_handler: done, key %s stop %d",
11748 gtk_accelerator_name(key->keyval, key->state), stop_emission);
11749 #endif
11750
11751 return (stop_emission);
11752 }
11753
11754 /*
11755 * gtk_sheet_key_press_handler:
11756 *
11757 * this is the #GtkSheet widget class "key-press-event" handler.
11758 *
11759 * @param widget the #GtkSheet
11760 * @param key the GdkEventKey which triggered this signal
11761 *
11762 * @return TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
11763 */
11764 static gboolean
11765 gtk_sheet_key_press_handler(GtkWidget *widget, GdkEventKey *key)
11766 {
11767 GtkSheet *sheet;
11768 gint state;
11769 gboolean extend_selection = FALSE;
11770 gboolean in_selection = FALSE;
11771
11772 sheet = GTK_SHEET(widget);
11773
11774 extend_selection = (key->state & GDK_SHIFT_MASK)
11775 || key->keyval == GDK_KEY_Shift_L
11776 || key->keyval == GDK_KEY_Shift_R;
11777
11778 state = sheet->state;
11779 in_selection = GTK_SHEET_IN_SELECTION(sheet);
11780 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_SELECTION);
11781
11782 #if GTK_SHEET_DEBUG_KEYPRESS > 0
11783 g_debug("gtk_sheet_key_press_handler: key %s",
11784 gtk_accelerator_name(key->keyval, key->state));
11785 #endif
11786
11787 /* if there is a key_binding, use implementation from _gtk_sheet_move_cursor() */
11788
11789 if (_gtk_sheet_binding_filter(sheet, key)
11790 && gtk_bindings_activate_event(GTK_OBJECT(sheet), key))
11791 {
11792 #if GTK_SHEET_DEBUG_KEYPRESS > 0
11793 g_debug("gtk_sheet_key_press_handler: done %s (binding)",
11794 gtk_accelerator_name(key->keyval, key->state));
11795 #endif
11796 return (TRUE);
11797 }
11798
11799 return (FALSE);
11800 }
11801
11802
11803 /**
11804 * _gtk_sheet_move_cursor:
11805 * @sheet: the sheet
11806 * @step: type of movement
11807 * @count: number of steps to move
11808 * @extend_selection: TRUE if the move should extend the
11809 * selection default handler for move-cursor signal
11810 */
11811 static void _gtk_sheet_move_cursor(GtkSheet *sheet,
11812 GtkMovementStep step,
11813 gint count,
11814 gboolean extend_selection)
11815 {
11816 gint row, col;
11817 gboolean veto = TRUE;
11818
11819 #if GTK_SHEET_DEBUG_SIGNALS > 0
11820 g_debug("SIGNAL _gtk_sheet_move_cursor %p step %d count %d extend %d",
11821 sheet, step, count, extend_selection);
11822 #endif
11823
11824 row = sheet->active_cell.row;
11825 col = sheet->active_cell.col;
11826
11827 switch(step)
11828 {
11829 case GTK_MOVEMENT_PAGES:
11830 count = count *
11831 (MAX_VIEW_ROW(sheet) - MIN_VIEW_ROW(sheet) - GTK_SHEET_PAGE_OVERLAP);
11832 /* FALLTHROUGH */
11833
11834 case GTK_MOVEMENT_DISPLAY_LINES:
11835 if (count < 0) /* Move Up */
11836 {
11837 if (extend_selection)
11838 {
11839 if (sheet->state == GTK_STATE_NORMAL)
11840 {
11841 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
11842 if (!veto)
11843 break;
11844 }
11845 if (sheet->selection_cell.row > 0)
11846 {
11847 row = sheet->selection_cell.row + count;
11848 _HUNT_VISIBLE_UP(row);
11849 gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col);
11850 }
11851 return;
11852 }
11853
11854 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
11855 row = MIN_VIEW_ROW(sheet);
11856 if (sheet->state == GTK_SHEET_ROW_SELECTED)
11857 col = MIN_VIEW_COLUMN(sheet);
11858
11859 if (row > 0)
11860 {
11861 row = row + count;
11862 _HUNT_FOCUS_UP(row,col);
11863 }
11864 }
11865 else if (count > 0) /* Move Down */
11866 {
11867 if (extend_selection)
11868 {
11869 if (sheet->state == GTK_STATE_NORMAL)
11870 {
11871 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
11872 if (!veto)
11873 break;
11874 }
11875 if (sheet->selection_cell.row < sheet->maxrow)
11876 {
11877 row = sheet->selection_cell.row + count;
11878 _HUNT_VISIBLE_DOWN(row);
11879 gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col);
11880 }
11881 return;
11882 }
11883
11884 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
11885 row = MIN_VIEW_ROW(sheet) - 1;
11886 if (sheet->state == GTK_SHEET_ROW_SELECTED)
11887 col = MIN_VIEW_COLUMN(sheet);
11888
11889 if (row < sheet->maxrow)
11890 {
11891 row = row + count;
11892 _HUNT_FOCUS_DOWN(row,col);
11893 }
11894 }
11895 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
11896 break;
11897
11898 case GTK_MOVEMENT_HORIZONTAL_PAGES:
11899 count = count *
11900 (MAX_VIEW_COLUMN(sheet) - MIN_VIEW_COLUMN(sheet) - GTK_SHEET_PAGE_OVERLAP);
11901 /* FALLTHROUGH */
11902
11903 case GTK_MOVEMENT_VISUAL_POSITIONS:
11904 if (count < 0) /* Move Left */
11905 {
11906 if (extend_selection)
11907 {
11908 if (sheet->state == GTK_STATE_NORMAL)
11909 {
11910 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
11911 if (!veto)
11912 break;
11913 }
11914 if (sheet->selection_cell.col > 0)
11915 {
11916 col = sheet->selection_cell.col + count;
11917 _HUNT_VISIBLE_LEFT(col);
11918 gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col);
11919 }
11920 return;
11921 }
11922
11923 if (sheet->state == GTK_SHEET_ROW_SELECTED)
11924 col = MIN_VIEW_COLUMN(sheet) - 1;
11925 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
11926 row = MIN_VIEW_ROW(sheet);
11927
11928 if (col > 0)
11929 {
11930 col = col + count;
11931 _HUNT_FOCUS_LEFT(row,col);
11932 }
11933 }
11934 else if (count > 0) /* Move Right */
11935 {
11936 if (extend_selection)
11937 {
11938 if (sheet->state == GTK_STATE_NORMAL)
11939 {
11940 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
11941 if (!veto)
11942 break;
11943 }
11944 if (sheet->selection_cell.col < sheet->maxcol)
11945 {
11946 col = sheet->selection_cell.col + count;
11947 _HUNT_VISIBLE_RIGHT(col);
11948 gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col);
11949 }
11950 return;
11951 }
11952
11953 if (sheet->state == GTK_SHEET_ROW_SELECTED)
11954 col = MIN_VIEW_COLUMN(sheet) - 1;
11955 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
11956 row = MIN_VIEW_ROW(sheet);
11957
11958 if (col < sheet->maxcol)
11959 {
11960 /* after sheet initialisation, row is -1
11961 when called from gtk_sheet_focus()
11962 */
11963 if (row < 0) row = 0;
11964
11965 col = col + count;
11966 _HUNT_FOCUS_RIGHT(row,col);
11967 }
11968 }
11969 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
11970 break;
11971
11972 case GTK_MOVEMENT_BUFFER_ENDS:
11973 if (count < 0) /* Topmost Row */
11974 {
11975 if (extend_selection)
11976 {
11977 if (sheet->state == GTK_STATE_NORMAL)
11978 {
11979 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
11980 if (!veto)
11981 break;
11982 }
11983 if (sheet->selection_cell.row > 0)
11984 {
11985 row = 0;
11986 _HUNT_VISIBLE_UP(row);
11987 gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col);
11988 }
11989 return;
11990 }
11991 row = 0;
11992 _HUNT_FOCUS_UP(row,col);
11993 }
11994 else if (count > 0) /* Last Row */
11995 {
11996 if (extend_selection)
11997 {
11998 if (sheet->state == GTK_STATE_NORMAL)
11999 {
12000 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
12001 if (!veto)
12002 break;
12003 }
12004 if (sheet->selection_cell.row < sheet->maxrow)
12005 {
12006 row = sheet->maxrow;
12007 _HUNT_VISIBLE_DOWN(row);
12008 gtk_sheet_extend_selection(sheet, row, sheet->selection_cell.col);
12009 }
12010 return;
12011 }
12012 row = sheet->maxrow;
12013 _HUNT_FOCUS_DOWN(row,col);
12014 }
12015 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
12016 break;
12017
12018 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
12019 if (count < 0) /* First Column */
12020 {
12021 if (extend_selection)
12022 {
12023 if (sheet->state == GTK_STATE_NORMAL)
12024 {
12025 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
12026 if (!veto)
12027 break;
12028 }
12029 if (sheet->selection_cell.col > 0)
12030 {
12031 col = 0;
12032 _HUNT_VISIBLE_LEFT(col);
12033 gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col);
12034 }
12035 return;
12036 }
12037 col = 0;
12038 _HUNT_FOCUS_LEFT(row,col);
12039 }
12040 else if (count > 0) /* Last Column */
12041 {
12042 if (extend_selection)
12043 {
12044 if (sheet->state == GTK_STATE_NORMAL)
12045 {
12046 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
12047 if (!veto)
12048 break;
12049 }
12050 if (sheet->selection_cell.col < sheet->maxcol)
12051 {
12052 col = sheet->maxcol;
12053 _HUNT_VISIBLE_RIGHT(col);
12054 gtk_sheet_extend_selection(sheet, sheet->selection_cell.row, col);
12055 }
12056 return;
12057 }
12058 col = sheet->maxcol;
12059 _HUNT_FOCUS_RIGHT(row,col);
12060 }
12061 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
12062 break;
12063
12064 case GTK_MOVEMENT_LOGICAL_POSITIONS:
12065 if (sheet->state == GTK_SHEET_ROW_SELECTED)
12066 col = MIN_VIEW_COLUMN(sheet) - 1;
12067 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
12068 row = MIN_VIEW_ROW(sheet);
12069
12070 if ((count == GTK_DIR_LEFT) /* Tab horizontal backward */
12071 || (count == GTK_DIR_TAB_BACKWARD)) /* Tab horizontal backward */
12072 {
12073 gint old_col = col;
12074 gint old_row = row;
12075
12076 if (col > 0) /* move left within line */
12077 {
12078 col = col - 1;
12079 _HUNT_FOCUS_LEFT(row,col);
12080 }
12081 if (col == old_col && row > 0) /* wrap at eol */
12082 {
12083 row = row - 1;
12084 _HUNT_FOCUS_UP(row,col);
12085
12086 if (row != old_row)
12087 {
12088 col = sheet->maxcol;
12089 _HUNT_FOCUS_LEFT(row,col);
12090 }
12091 }
12092 }
12093 else if ((count == GTK_DIR_RIGHT) /* Tab horizontal forward */
12094 || (count == GTK_DIR_TAB_FORWARD))
12095 {
12096 gint old_col = col;
12097
12098 if (col < sheet->maxcol) /* move right within line */
12099 {
12100 col = col + 1;
12101 _HUNT_FOCUS_RIGHT(row,col);
12102 }
12103 if (col == old_col && row < sheet->maxrow) /* wrap at eol */
12104 {
12105 col = 0;
12106 _HUNT_FOCUS_RIGHT(row,col);
12107
12108 row = row + 1;
12109 _HUNT_FOCUS_DOWN(row,col);
12110 }
12111 }
12112 else if (count == GTK_DIR_UP) /* Tab vertical backward */
12113 {
12114 gint old_row = row;
12115 gint old_col = col;
12116
12117 if (row > 0) /* move up within column */
12118 {
12119 row = row - 1;
12120 _HUNT_FOCUS_UP(row,col);
12121 }
12122 if (row == old_row && col > 0) /* wrap at eol */
12123 {
12124 col = col - 1;
12125 _HUNT_FOCUS_LEFT(row,col);
12126
12127 if (col != old_col)
12128 {
12129 row = sheet->maxrow;
12130 _HUNT_FOCUS_UP(row,col);
12131 }
12132 }
12133 }
12134 else if (count == GTK_DIR_DOWN) /* Tab vertical forward */
12135 {
12136 gint old_row = row;
12137 gint old_col = col;
12138
12139 if (row < sheet->maxrow) /* move down within column */
12140 {
12141 row = row + 1;
12142 _HUNT_FOCUS_DOWN(row,col);
12143
12144 }
12145 if (row == old_row && col < sheet->maxcol) /* wrap at eol */
12146 {
12147 col = col + 1;
12148 _HUNT_FOCUS_RIGHT(row,col);
12149 if (col != old_col)
12150 {
12151 row = 0;
12152 _HUNT_FOCUS_DOWN(row,col);
12153 }
12154 }
12155 }
12156 if( (row>=0) && (col>=0) ) gtk_sheet_click_cell(sheet, row, col, &veto);
12157 break;
12158
12159 default:
12160 break;
12161 }
12162
12163 gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col);
12164
12165 #if GTK_SHEET_DEBUG_SIGNALS > 0
12166 g_debug("SIGNAL _gtk_sheet_move_cursor %p done", sheet);
12167 #endif
12168 }
12169
12170 /*
12171 * gtk_sheet_size_request_handler:
12172 *
12173 * this is the #GtkSheet widget class "size-request" signal handler
12174 *
12175 * @param widget the #GtkSheet
12176 * @param requisition
12177 * the #GtkRequisition
12178 */
12179 static void
12180 gtk_sheet_size_request_handler(GtkWidget *widget, GtkRequisition *requisition)
12181 {
12182 GtkSheet *sheet;
12183 GList *children;
12184 GtkSheetChild *child;
12185 GtkRequisition child_requisition;
12186
12187 g_return_if_fail(widget != NULL);
12188 g_return_if_fail(GTK_IS_SHEET(widget));
12189 g_return_if_fail(requisition != NULL);
12190
12191 #if GTK_SHEET_DEBUG_SIZE > 0
12192 g_debug("gtk_sheet_size_request_handler: called");
12193 #endif
12194
12195 sheet = GTK_SHEET(widget);
12196
12197 requisition->width = 3 * GTK_SHEET_COLUMN_DEFAULT_WIDTH;
12198 requisition->height = 3 * _gtk_sheet_row_default_height(widget);
12199
12200 /* compute the size of the column title area */
12201 if (sheet->column_titles_visible)
12202 requisition->height += sheet->column_title_area.height;
12203
12204 /* compute the size of the row title area */
12205 if (sheet->row_titles_visible)
12206 requisition->width += sheet->row_title_area.width;
12207
12208 _gtk_sheet_recalc_view_range(sheet);
12209
12210 children = sheet->children;
12211 while (children)
12212 {
12213 child = children->data;
12214 children = children->next;
12215
12216 gtk_widget_size_request(child->widget, &child_requisition);
12217 }
12218 }
12219
12220
12221 /*
12222 * gtk_sheet_size_allocate_handler:
12223 *
12224 * this is the #GtkSheet widget class "size-allocate" signal handler
12225 *
12226 * @param widget the #GtkSheet
12227 * @param allocation the #GtkAllocation
12228 */
12229 static void
12230 gtk_sheet_size_allocate_handler(GtkWidget *widget, GtkAllocation *allocation)
12231 {
12232 GtkSheet *sheet;
12233 GtkAllocation sheet_allocation;
12234 gint border_width;
12235 gboolean modified;
12236
12237 g_return_if_fail(widget != NULL);
12238 g_return_if_fail(GTK_IS_SHEET(widget));
12239 g_return_if_fail(allocation != NULL);
12240
12241 #if GTK_SHEET_DEBUG_SIZE > 0
12242 g_debug("gtk_sheet_size_allocate_handler: called (%d, %d, %d, %d)",
12243 allocation->x, allocation->y, allocation->width, allocation->height);
12244 #endif
12245
12246 sheet = GTK_SHEET(widget);
12247
12248 gtk_widget_set_allocation(widget, allocation);
12249 border_width = gtk_container_get_border_width(GTK_CONTAINER(widget));
12250
12251 if (gtk_widget_get_realized(widget))
12252 {
12253 gdk_window_move_resize(gtk_widget_get_window(widget),
12254 allocation->x + border_width,
12255 allocation->y + border_width,
12256 allocation->width - 2 * border_width,
12257 allocation->height - 2 * border_width);
12258 }
12259
12260 /* use internal allocation structure for all the math
12261 * because it's easier than always subtracting the container
12262 * border width */
12263
12264 sheet->internal_allocation.x = 0;
12265 sheet->internal_allocation.y = 0;
12266 sheet->internal_allocation.width = allocation->width - 2 * border_width;
12267 sheet->internal_allocation.height = allocation->height - 2 * border_width;
12268
12269 sheet_allocation.x = 0;
12270 sheet_allocation.y = 0;
12271 sheet_allocation.width = allocation->width - 2 * border_width;
12272 sheet_allocation.height = allocation->height - 2 * border_width;
12273
12274 modified =
12275 (sheet->sheet_window_width != sheet_allocation.width) ||
12276 (sheet->sheet_window_height != sheet_allocation.height);
12277
12278 sheet->sheet_window_width = sheet_allocation.width;
12279 sheet->sheet_window_height = sheet_allocation.height;
12280
12281 if (gtk_widget_get_realized(widget))
12282 {
12283 gdk_window_move_resize(sheet->sheet_window,
12284 sheet_allocation.x,
12285 sheet_allocation.y,
12286 sheet_allocation.width,
12287 sheet_allocation.height);
12288 }
12289
12290 /* position the window which holds the column title buttons */
12291
12292 sheet->column_title_area.x = 0;
12293 sheet->column_title_area.y = 0;
12294 if (sheet->row_titles_visible)
12295 sheet->column_title_area.x = sheet->row_title_area.width;
12296 sheet->column_title_area.width = sheet_allocation.width - sheet->column_title_area.x;
12297
12298 if (gtk_widget_get_realized(widget) && sheet->column_titles_visible)
12299 {
12300 gdk_window_move_resize(sheet->column_title_window,
12301 sheet->column_title_area.x,
12302 sheet->column_title_area.y,
12303 sheet->column_title_area.width,
12304 sheet->column_title_area.height);
12305 }
12306
12307 /* column button allocation */
12308 _gtk_sheet_column_buttons_size_allocate(sheet);
12309
12310 /* position the window which holds the row title buttons */
12311 sheet->row_title_area.x = 0;
12312 sheet->row_title_area.y = 0;
12313 if (sheet->column_titles_visible)
12314 sheet->row_title_area.y = sheet->column_title_area.height;
12315 sheet->row_title_area.height = sheet_allocation.height - sheet->row_title_area.y;
12316
12317 if (gtk_widget_get_realized(widget) && sheet->row_titles_visible)
12318 {
12319 gdk_window_move_resize(sheet->row_title_window,
12320 sheet->row_title_area.x,
12321 sheet->row_title_area.y,
12322 sheet->row_title_area.width,
12323 sheet->row_title_area.height);
12324 }
12325
12326 /* row button allocation */
12327 size_allocate_row_title_buttons(sheet);
12328
12329 if (gtk_sheet_autoresize(sheet) &&
12330 (modified || (GTK_SHEET_FLAGS(sheet) & GTK_SHEET_IN_AUTORESIZE_PENDING)))
12331 {
12332 /* autoresize here, because window was changed -> max col_width */
12333 gtk_sheet_autoresize_all(sheet);
12334 GTK_SHEET_UNSET_FLAGS(sheet, GTK_SHEET_IN_AUTORESIZE_PENDING);
12335 }
12336
12337 _gtk_sheet_recalc_view_range(sheet);
12338
12339 /* re-scale backing pixmap */
12340 gtk_sheet_make_backing_pixmap(sheet, 0, 0);
12341 gtk_sheet_position_children(sheet);
12342
12343 /* set the scrollbars adjustments */
12344 _gtk_sheet_scrollbar_adjust(sheet);
12345 }
12346
12347 static gboolean gtk_sheet_focus(GtkWidget *widget,
12348 GtkDirectionType direction)
12349 {
12350 g_return_val_if_fail(GTK_IS_SHEET(widget), FALSE);
12351 GtkSheet *sheet = GTK_SHEET(widget);
12352
12353 if (!gtk_widget_is_sensitive(GTK_WIDGET(sheet))) {
12354 g_debug("gtk_sheet_focus: not sensitive");
12355 return(FALSE);
12356 }
12357
12358 #if GTK_SHEET_DEBUG_KEYPRESS > 0
12359 g_debug("gtk_sheet_focus: %p %d", widget, direction);
12360 #endif
12361
12362 if (!gtk_widget_has_focus (widget))
12363 {
12364 gtk_widget_grab_focus (widget);
12365 }
12366
12367 gint row = sheet->active_cell.row;
12368 gint col = sheet->active_cell.col;
12369
12370 if (row < 0 || col < 0) /* not in sheet */
12371 {
12372 _gtk_sheet_move_cursor(sheet, GTK_MOVEMENT_VISUAL_POSITIONS, 1, FALSE);
12373 return(TRUE);
12374 }
12375
12376 gboolean veto;
12377
12378 gtk_sheet_click_cell(sheet, row, col, &veto);
12379 if (!veto) return(FALSE);
12380
12381 return(TRUE);
12382 }
12383
12384
12385 static void
12386 size_allocate_row_title_buttons(GtkSheet *sheet)
12387 {
12388 gint i, y, height;
12389 GdkRectangle *rta = &sheet->row_title_area;
12390
12391 if (!sheet->row_titles_visible)
12392 return;
12393 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
12394 return;
12395
12396 #if GTK_SHEET_DEBUG_ALLOCATION > 0
12397 g_debug("size_allocate_row_title_buttons: called");
12398 #endif
12399
12400 height = sheet->sheet_window_height;
12401 y = 0;
12402
12403 if (sheet->column_titles_visible)
12404 {
12405 height -= sheet->column_title_area.height;
12406 y = sheet->column_title_area.height;
12407 }
12408
12409 /* if neccessary, resize the row title window */
12410 if (rta->height != height || rta->y != y)
12411 {
12412 rta->y = y;
12413 rta->height = height;
12414 gdk_window_move_resize(sheet->row_title_window,
12415 rta->x,
12416 rta->y,
12417 rta->width,
12418 rta->height);
12419 }
12420
12421 /* if the right edge of the sheet is visible, clear it */
12422 if (MAX_VIEW_ROW(sheet) >= sheet->maxrow)
12423 {
12424 /* this one causes flicker */
12425
12426 gdk_window_clear_area(sheet->row_title_window,
12427 0, 0,
12428 rta->width,
12429 rta->height);
12430 }
12431
12432 if (!gtk_widget_is_drawable(GTK_WIDGET(sheet)))
12433 return;
12434
12435 for (i = MIN_VIEW_ROW(sheet); i <= MAX_VIEW_ROW(sheet) && i <= sheet->maxrow; i++) _gtk_sheet_draw_button(sheet, i, -1);
12436 }
12437
12438 /**
12439 * gtk_sheet_recalc_top_ypixels:
12440 * @sheet: the #GtkSheet
12441 *
12442 * recalculate topmost pixel of all rows
12443 */
12444 void
12445 _gtk_sheet_recalc_top_ypixels(GtkSheet *sheet)
12446 {
12447 gint i, cy;
12448
12449 if (sheet->column_titles_visible)
12450 cy = sheet->column_title_area.height;
12451 else
12452 cy = 0;
12453
12454 for (i = 0; i <= sheet->maxrow; i++)
12455 {
12456 sheet->row[i].top_ypixel = cy;
12457 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
12458 cy += sheet->row[i].height;
12459 }
12460 }
12461
12462 /**
12463 * _gtk_sheet_recalc_left_xpixels:
12464 * @sheet: the #GtkSheet
12465 *
12466 * recalculate left pixel index of all columns
12467 */
12468 void
12469 _gtk_sheet_recalc_left_xpixels(GtkSheet *sheet)
12470 {
12471 gint i, cx;
12472
12473 if (sheet->row_titles_visible)
12474 cx = sheet->row_title_area.width;
12475 else
12476 cx = 0;
12477
12478 for (i = 0; i <= sheet->maxcol; i++)
12479 {
12480 COLPTR(sheet, i)->left_xpixel = cx;
12481 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, i)))
12482 cx += COLPTR(sheet, i)->width;
12483 }
12484 }
12485
12486 /**
12487 * _gtk_sheet_reset_text_column:
12488 * @sheet: the #GtkSheet
12489 * @start_column: left most column to start from
12490 *
12491 * reset left/right text column index to initial state
12492 */
12493 void
12494 _gtk_sheet_reset_text_column(GtkSheet *sheet, gint start_column)
12495 {
12496 #if GTK_SHEET_OPTIMIZE_COLUMN_DRAW>0
12497 int i;
12498
12499 g_assert(start_column >= -1);
12500
12501 for (i = start_column + 1; i <= sheet->maxcol; i++) /* for the fresh columns */
12502 {
12503 GtkSheetColumn *colptr = COLPTR(sheet, i);
12504
12505 colptr->left_text_column = i;
12506 colptr->right_text_column = i;
12507 }
12508 #endif
12509 }
12510
12511 static void
12512 _get_entry_window_size(GtkEntry *entry,
12513 gint *x,
12514 gint *y,
12515 gint *width,
12516 gint *height)
12517 {
12518 GtkRequisition requisition;
12519 GtkAllocation allocation;
12520 GtkWidget *widget = GTK_WIDGET(entry);
12521
12522 /* GtkEntry->is_cell_renderer is a GSEAL()ed structure member.
12523 It will only be set to TRUE when calling the GtkEntry's GtkCellEditable
12524 interface function gtk_entry_cell_editable_init().
12525
12526 This should never be the case in GtkSheet or GtkItemEntry.
12527
12528 Anyhow, if the above statement wasn't true, solutions could be:
12529 - ask the Gtk maintainers to add the function
12530 GtkEntry::get_widget_window_size() to the public GtkEntry interface
12531 - last resort work-around:
12532 use the sealed member anyhow GtkEntry->GSEAL(is_cell_renderer)
12533 */
12534 #if 0
12535 # define ENTRY_IS_CELL_RENDERER entry->is_cell_renderer
12536 #else
12537 # define ENTRY_IS_CELL_RENDERER FALSE
12538 #endif
12539
12540 gtk_widget_get_child_requisition(widget, &requisition);
12541 gtk_widget_get_allocation(widget, &allocation);
12542
12543 if (x)
12544 *x = allocation.x;
12545
12546 if (y)
12547 {
12548 if (ENTRY_IS_CELL_RENDERER)
12549 *y = allocation.y;
12550 else
12551 *y = allocation.y + (allocation.height - requisition.height) / 2;
12552 }
12553
12554 if (width)
12555 *width = allocation.width;
12556
12557 if (height)
12558 {
12559 if (ENTRY_IS_CELL_RENDERER)
12560 *height = allocation.height;
12561 else
12562 *height = requisition.height;
12563 }
12564 }
12565
12566
12567
12568 /**
12569 * _gtk_sheet_entry_size_allocate:
12570 * @sheet: the #GtkSheet
12571 *
12572 * size allocation handler for the sheet entry
12573 */
12574 void
12575 _gtk_sheet_entry_size_allocate(GtkSheet *sheet)
12576 {
12577 GtkAllocation shentry_allocation;
12578 gint row, col;
12579 gint size, entry_max_size, column_width, row_height;
12580 guint text_width, text_height;
12581
12582 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
12583 return;
12584 if (!gtk_widget_get_mapped(GTK_WIDGET(sheet)))
12585 return;
12586 if (sheet->maxrow < 0 || sheet->maxcol < 0)
12587 return;
12588 if (!sheet->sheet_entry) /* PR#102114 */
12589 return;
12590
12591 #if GTK_SHEET_DEBUG_SIZE > 0
12592 g_debug("_gtk_sheet_entry_size_allocate: called");
12593 #endif
12594
12595 GtkWidget *entry_widget = gtk_sheet_get_entry(sheet);
12596
12597 #if 0
12598 /* the entry setup must be done before the text assigment,
12599 otherwise max_length may cause text assignment to fail
12600 */
12601 _gtk_sheet_entry_setup(sheet,
12602 sheet->active_cell.row, sheet->active_cell.col,
12603 entry_widget);
12604 #endif
12605
12606 GtkSheetCellAttr attributes;
12607 gtk_sheet_get_attributes(sheet,
12608 sheet->active_cell.row, sheet->active_cell.col,
12609 &attributes);
12610
12611 if (gtk_widget_get_realized(sheet->sheet_entry))
12612 {
12613 gtk_widget_size_request(sheet->sheet_entry, NULL);
12614 }
12615
12616 if (GTK_IS_ITEM_ENTRY(entry_widget))
12617 {
12618 entry_max_size = GTK_ITEM_ENTRY(entry_widget)->text_max_size;
12619 }
12620 else
12621 entry_max_size = 0;
12622
12623 row = sheet->active_cell.row;
12624 col = sheet->active_cell.col;
12625
12626 text_width = 0;
12627 text_height = 0;
12628 {
12629 gchar *text = gtk_sheet_get_entry_text(sheet);
12630
12631 if (text && text[0])
12632 {
12633 _get_string_extent(sheet,
12634 (0 <= col && col <= sheet->maxcol) ? COLPTR(sheet, col) : NULL,
12635 attributes.font_desc, text, &text_width, &text_height);
12636 }
12637
12638 g_free(text);
12639 }
12640
12641 if (0 <= col && col <= sheet->maxcol)
12642 column_width = COLPTR(sheet, col)->width;
12643 else
12644 column_width = GTK_SHEET_COLUMN_DEFAULT_WIDTH;
12645
12646 if (0 <= row && row <= sheet->maxrow)
12647 row_height = sheet->row[row].height;
12648 else
12649 row_height = GTK_SHEET_ROW_DEFAULT_HEIGHT;
12650
12651 size = MIN(text_width, entry_max_size);
12652 size = MAX(size, column_width - 2 * CELLOFFSET);
12653
12654 shentry_allocation.x = _gtk_sheet_column_left_xpixel(sheet, col);
12655 shentry_allocation.y = _gtk_sheet_row_top_ypixel(sheet, row);
12656 shentry_allocation.width = column_width;
12657 shentry_allocation.height = row_height;
12658
12659 if (GTK_IS_ITEM_ENTRY(sheet->sheet_entry))
12660 {
12661 #if GTK_SHEET_DEBUG_SIZE > 0
12662 g_debug("_gtk_sheet_entry_size_allocate: is_item_entry");
12663 #endif
12664
12665 shentry_allocation.height -= 2 * CELLOFFSET;
12666 shentry_allocation.y += CELLOFFSET;
12667
12668 if (gtk_sheet_clip_text(sheet))
12669 shentry_allocation.width = column_width - 2 * CELLOFFSET;
12670 else /* text extends multiple cells */
12671 shentry_allocation.width = size;
12672
12673 switch(GTK_ITEM_ENTRY(entry_widget)->justification)
12674 {
12675 case GTK_JUSTIFY_CENTER:
12676 shentry_allocation.x += (column_width) / 2 - size / 2;
12677 break;
12678
12679 case GTK_JUSTIFY_RIGHT:
12680 shentry_allocation.x += column_width - size;
12681 break;
12682
12683 case GTK_JUSTIFY_LEFT:
12684 case GTK_JUSTIFY_FILL:
12685 shentry_allocation.x += CELLOFFSET;
12686 break;
12687 }
12688
12689 /* vertical justification */
12690 {
12691 gint x, y, width, height;
12692
12693 _get_entry_window_size(GTK_ENTRY(entry_widget), &x, &y, &width, &height);
12694
12695 #if GTK_SHEET_DEBUG_SIZE>0
12696 g_debug("_gtk_sheet_entry_size_allocate: get_widget_window_size (%d, %d, %d, %d)",
12697 x, y, width, height);
12698 #endif
12699
12700 switch(sheet->vjust)
12701 {
12702 case GTK_SHEET_VERTICAL_JUSTIFICATION_DEFAULT:
12703 case GTK_SHEET_VERTICAL_JUSTIFICATION_TOP:
12704 shentry_allocation.height = height;
12705 break;
12706
12707 case GTK_SHEET_VERTICAL_JUSTIFICATION_MIDDLE:
12708 shentry_allocation.height = shentry_allocation.height / 2;
12709 break;
12710
12711 case GTK_SHEET_VERTICAL_JUSTIFICATION_BOTTOM:
12712 break;
12713 }
12714 }
12715 }
12716 else if ( GTK_IS_DATA_TEXT_VIEW(sheet->sheet_entry)
12717 || GTK_IS_TEXT_VIEW(sheet->sheet_entry) )
12718 {
12719 #if GTK_SHEET_DEBUG_SIZE > 0
12720 g_debug("_gtk_sheet_entry_size_allocate: is_text_view");
12721 #endif
12722
12723 shentry_allocation.height -= 2 * CELLOFFSET;
12724 shentry_allocation.y += CELLOFFSET;
12725 shentry_allocation.x += CELLOFFSET;
12726
12727 if (gtk_sheet_clip_text(sheet))
12728 shentry_allocation.width = column_width - 2 * CELLOFFSET;
12729 else /* text extends multiple cells */
12730 shentry_allocation.width = size;
12731 }
12732 else
12733 {
12734 shentry_allocation.x += 2;
12735 shentry_allocation.y += 2;
12736 shentry_allocation.width -= MIN(shentry_allocation.width, 3);
12737 shentry_allocation.height -= MIN(shentry_allocation.height, 3);
12738 }
12739
12740 #if 0
12741 /* the following works with most widgets, but looks funny */
12742 gtk_widget_set_size_request(sheet->sheet_entry,
12743 shentry_allocation.width, shentry_allocation.height);
12744 #endif
12745
12746 #if GTK_SHEET_DEBUG_SIZE >0
12747 g_debug("_gtk_sheet_entry_size_allocate: allocate (%d,%d,%d,%d)",
12748 shentry_allocation.x, shentry_allocation.y, shentry_allocation.width, shentry_allocation.height);
12749 #endif
12750
12751 gtk_widget_size_allocate(sheet->sheet_entry, &shentry_allocation);
12752
12753 #if GTK_SHEET_DEBUG_SIZE > 0
12754 g_debug("_gtk_sheet_entry_size_allocate: returned (%d,%d,%d,%d)",
12755 shentry_allocation.x, shentry_allocation.y, shentry_allocation.width, shentry_allocation.height);
12756 #endif
12757 }
12758
12759 #if GTK_SHEET_DEBUG_FINALIZE > 0
12760 /* obj_ref debug code */
12761 static void weak_notify(gpointer data, GObject *o)
12762 {
12763 gchar *msg = data; /* assume a string was passed as data */
12764
12765 g_debug("weak_notify: %p finalized (%s)", o, msg ? msg : "");
12766 }
12767 #endif
12768
12769 static void
12770 gtk_sheet_entry_set_max_size(GtkSheet *sheet)
12771 {
12772 gint i;
12773 gint size = 0;
12774 gint sizel = 0, sizer = 0;
12775 gint row, col;
12776 GtkJustification justification;
12777
12778 row = sheet->active_cell.row;
12779 col = sheet->active_cell.col;
12780
12781 #if GTK_SHEET_DEBUG_SIZE > 0
12782 g_debug("gtk_sheet_entry_set_max_size: called");
12783 #endif
12784
12785 if (!GTK_IS_ITEM_ENTRY(sheet->sheet_entry) || gtk_sheet_clip_text(sheet))
12786 return;
12787
12788 justification = GTK_ITEM_ENTRY(sheet->sheet_entry)->justification;
12789
12790 switch(justification)
12791 {
12792 case GTK_JUSTIFY_FILL:
12793 case GTK_JUSTIFY_LEFT:
12794 for (i = col + 1; i <= MAX_VIEW_COLUMN(sheet) && i <= sheet->maxcol; i++)
12795 {
12796 if (gtk_sheet_cell_get_text(sheet, row, i))
12797 break;
12798
12799 size += COLPTR(sheet, i)->width;
12800 }
12801 size = MIN(size, sheet->sheet_window_width - _gtk_sheet_column_left_xpixel(sheet, col));
12802 break;
12803
12804 case GTK_JUSTIFY_RIGHT:
12805 for (i = col - 1; i >= MIN_VIEW_COLUMN(sheet); i--)
12806 {
12807 if (gtk_sheet_cell_get_text(sheet, row, i))
12808 break;
12809
12810 if (i < 0 || i > sheet->maxcol)
12811 continue;
12812 size += COLPTR(sheet, i)->width;
12813 }
12814 break;
12815
12816 case GTK_JUSTIFY_CENTER:
12817 for (i = col + 1; i <= MAX_VIEW_COLUMN(sheet) && i <= sheet->maxcol; i++)
12818 {
12819 /* if (gtk_sheet_cell_get_text(sheet, row, i)) break; */
12820
12821 sizer += COLPTR(sheet, i)->width;
12822 }
12823 for (i = col - 1; i >= MIN_VIEW_COLUMN(sheet); i--)
12824 {
12825 if (gtk_sheet_cell_get_text(sheet, row, i))
12826 break;
12827
12828 if (i < 0 || i > sheet->maxcol)
12829 continue;
12830 sizel += COLPTR(sheet, i)->width;
12831 }
12832 size = 2 * MIN(sizel, sizer);
12833 break;
12834 }
12835
12836 if (size != 0)
12837 size += COLPTR(sheet, col)->width;
12838 GTK_ITEM_ENTRY(sheet->sheet_entry)->text_max_size = size;
12839 }
12840
12841 static gboolean sheet_entry_focus_in_handler(GtkWidget *widget,
12842 GdkEventFocus *event, gpointer user_data)
12843 {
12844 gboolean retval = FALSE;
12845 g_signal_emit(GTK_OBJECT(widget),
12846 sheet_signals[ENTRY_FOCUS_IN], 0, event, &retval);
12847 return (retval);
12848 }
12849
12850 static gboolean sheet_entry_focus_out_handler(GtkWidget *widget,
12851 GdkEventFocus *event, gpointer user_data)
12852 {
12853 gboolean retval = FALSE;
12854 g_signal_emit(GTK_OBJECT(widget),
12855 sheet_signals[ENTRY_FOCUS_OUT], 0, event, &retval);
12856 return (retval);
12857 }
12858
12859 static void sheet_entry_populate_popup_handler(GtkWidget *widget,
12860 GtkMenu *menu, gpointer user_data)
12861 {
12862 #if GTK_SHEET_DEBUG_SIGNALS > 0
12863 g_debug("sheet_entry_populate_popup_handler: menu %p", menu);
12864 #endif
12865
12866 g_signal_emit(GTK_OBJECT(widget),
12867 sheet_signals[ENTRY_POPULATE_POPUP], 0, menu);
12868 }
12869
12870 static void
12871 create_sheet_entry(GtkSheet *sheet, GType new_entry_type)
12872 {
12873 GtkWidget *widget;
12874 GtkWidget * entry, *new_entry;
12875 GtkStyle *style;
12876
12877 widget = GTK_WIDGET(sheet);
12878
12879 #if GTK_SHEET_DEBUG_ENTRY > 0
12880 g_debug("create_sheet_entry: called");
12881 #endif
12882
12883 style = gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(sheet)));
12884
12885 if (sheet->sheet_entry)
12886 {
12887 /* avoids warnings */
12888 g_object_ref(sheet->sheet_entry);
12889 gtk_widget_unparent(sheet->sheet_entry);
12890 #if GTK_SHEET_DEBUG_ENTRY > 0
12891 g_debug("create_sheet_entry: destroying old entry %p", sheet->sheet_entry);
12892 #endif
12893 gtk_widget_destroy(sheet->sheet_entry);
12894 sheet->sheet_entry = NULL;
12895 }
12896
12897 if (new_entry_type == G_TYPE_NONE) new_entry_type = G_TYPE_ITEM_ENTRY;
12898
12899 #if GTK_SHEET_DEBUG_ENTRY > 0
12900 g_debug("create_sheet_entry: new_entry type %s",
12901 g_type_name(new_entry_type));
12902 #endif
12903
12904 new_entry = gtk_widget_new(new_entry_type, NULL);
12905
12906 #if GTK_SHEET_DEBUG_ENTRY > 0
12907 g_debug("create_sheet_entry: got new_entry %p", new_entry);
12908 #endif
12909
12910 /* connect focus signal propagation handlers */
12911 g_signal_connect_swapped(new_entry, "focus-in-event",
12912 G_CALLBACK(sheet_entry_focus_in_handler), sheet);
12913 g_signal_connect_swapped(new_entry, "focus-out-event",
12914 G_CALLBACK(sheet_entry_focus_out_handler), sheet);
12915
12916 if (GTK_IS_ENTRY(new_entry)
12917 || GTK_IS_DATA_TEXT_VIEW(new_entry)
12918 || GTK_IS_TEXT_VIEW(new_entry))
12919 {
12920 g_signal_connect_swapped(new_entry, "populate-popup",
12921 G_CALLBACK(sheet_entry_populate_popup_handler), sheet);
12922 }
12923
12924 sheet->installed_entry_type = new_entry_type;
12925 sheet->sheet_entry = new_entry;
12926 entry = gtk_sheet_get_entry(sheet);
12927
12928 if (!entry) /* this was an unsupported entry type */
12929 {
12930 g_warning("Unsupported entry type - widget must contain an GtkEditable or GtkTextView");
12931 gtk_widget_destroy(new_entry);
12932
12933 new_entry = gtk_item_entry_new();
12934 sheet->sheet_entry = new_entry;
12935 sheet->installed_entry_type = G_TYPE_ITEM_ENTRY;
12936 }
12937 g_object_ref_sink(sheet->sheet_entry);
12938
12939 #if GTK_SHEET_DEBUG_FINALIZE > 0
12940 g_object_weak_ref(G_OBJECT(sheet->sheet_entry), weak_notify, "Sheet-Entry");
12941 #endif
12942
12943 if (gtk_widget_get_realized(GTK_WIDGET(sheet)))
12944 {
12945 gtk_widget_size_request(sheet->sheet_entry, NULL);
12946
12947 #if GTK_SHEET_DEBUG_ENTRY > 0
12948 g_debug("create_sheet_entry: sheet was realized");
12949 #endif
12950 gtk_widget_set_parent_window(sheet->sheet_entry, sheet->sheet_window);
12951 gtk_widget_set_parent(sheet->sheet_entry, GTK_WIDGET(sheet));
12952 gtk_widget_realize(sheet->sheet_entry);
12953 }
12954
12955 g_signal_connect_swapped(GTK_OBJECT(entry), "key_press_event",
12956 (void *)gtk_sheet_entry_key_press_handler,
12957 GTK_OBJECT(sheet));
12958
12959 gtk_widget_show(sheet->sheet_entry);
12960 }
12961
12962
12963 /**
12964 * gtk_sheet_get_entry_type:
12965 * @sheet: a #GtkSheet
12966 *
12967 * Get sheets entry type, if known
12968 *
12969 * Returns: a #GtkSheetEntryType or GTK_SHEET_ENTRY_TYPE_DEFAULT
12970 */
12971 GType
12972 gtk_sheet_get_entry_type(GtkSheet *sheet)
12973 {
12974 g_return_val_if_fail(sheet, GTK_SHEET_ENTRY_TYPE_DEFAULT);
12975 g_return_val_if_fail(GTK_IS_SHEET(sheet), GTK_SHEET_ENTRY_TYPE_DEFAULT);
12976
12977 return (sheet->entry_type);
12978 }
12979
12980 /**
12981 * gtk_sheet_get_entry:
12982 * @sheet: a #GtkSheet
12983 *
12984 * Get sheet's entry widget.
12985 *
12986 * If the entry widget is a container, the direct childs of the
12987 * container are searched for a valid entry widget. If you want
12988 * the container itself to be returned, you should use
12989 * #gtk_sheet_get_entry_widget() instead.
12990 *
12991 * Returns: (transfer none) a #GtkWidget or NULL
12992 */
12993 GtkWidget *
12994 gtk_sheet_get_entry(GtkSheet *sheet)
12995 {
12996 GtkWidget *parent;
12997 GtkWidget *entry = NULL;
12998 GtkTableChild *table_child;
12999 GtkBoxChild *box_child;
13000 GList *children = NULL;
13001
13002 g_return_val_if_fail(sheet != NULL, NULL);
13003 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
13004
13005 if (!sheet->sheet_entry) /* PR#102114 */
13006 return(NULL);
13007
13008 if (GTK_IS_EDITABLE(sheet->sheet_entry))
13009 return (sheet->sheet_entry);
13010 if (GTK_IS_DATA_TEXT_VIEW(sheet->sheet_entry))
13011 return (sheet->sheet_entry);
13012 if (GTK_IS_TEXT_VIEW(sheet->sheet_entry))
13013 return (sheet->sheet_entry);
13014
13015 parent = GTK_WIDGET(sheet->sheet_entry);
13016
13017 if (GTK_IS_TABLE(parent))
13018 children = GTK_TABLE(parent)->children;
13019 if (GTK_IS_BOX(parent))
13020 children = GTK_BOX(parent)->children;
13021
13022 if (!children)
13023 return (NULL);
13024
13025 while (children)
13026 {
13027 if (GTK_IS_TABLE(parent))
13028 {
13029 table_child = children->data;
13030 entry = table_child->widget;
13031 }
13032
13033 if (GTK_IS_BOX(parent))
13034 {
13035 box_child = children->data;
13036 entry = box_child->widget;
13037 }
13038
13039 if (GTK_IS_EDITABLE(entry))
13040 return (entry);
13041 if (GTK_IS_DATA_TEXT_VIEW(entry))
13042 return (entry);
13043 if (GTK_IS_TEXT_VIEW(entry))
13044 return (entry);
13045
13046 children = children->next;
13047 }
13048
13049 return (NULL);
13050 }
13051
13052 /**
13053 * gtk_sheet_get_entry_widget:
13054 * @sheet: a #GtkSheet
13055 *
13056 * Get sheet's entry widget.
13057 *
13058 * If the entry widget is a container, the container widget is
13059 * returned. In order to get the entry in the container child,
13060 * you might want to use #gtk_sheet_get_entry() instead.
13061 *
13062 * Returns: (transfer none) a #GtkWidget or NULL
13063 */
13064 GtkWidget *
13065 gtk_sheet_get_entry_widget(GtkSheet *sheet)
13066 {
13067 g_return_val_if_fail(sheet != NULL, NULL);
13068 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
13069 g_return_val_if_fail(sheet->sheet_entry != NULL, NULL);
13070
13071 return (sheet->sheet_entry);
13072 }
13073
13074 /**
13075 * gtk_sheet_get_entry_text:
13076 * @sheet: a #GtkSheet
13077 *
13078 * Get the text out of the sheet_entry.
13079 *
13080 * This function is mainly used to synchronize the text of a
13081 * second entry with the sheet_entry.
13082 *
13083 * This function is necessary, because not all possible entry
13084 * widgets implement the GtkEditable interface yet.
13085 *
13086 * Returns: a copy of the sheet_entry text or NULL. This
13087 * function returns an allocated string, so g_free() it after
13088 * usage!
13089 */
13090 gchar *gtk_sheet_get_entry_text(GtkSheet *sheet)
13091 {
13092 gchar *text = NULL;
13093 GtkWidget *entry = NULL;
13094
13095 g_return_val_if_fail(sheet != NULL, NULL);
13096 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
13097
13098 #if GTK_SHEET_DEBUG_SET_ENTRY_TEXT > 0
13099 g_debug("gtk_sheet_get_entry_text[%p]", sheet);
13100 #endif
13101
13102 if (!sheet->sheet_entry) /* PR#102114 */
13103 return(NULL);
13104
13105 entry = gtk_sheet_get_entry(sheet);
13106 g_return_val_if_fail(entry != NULL, NULL);
13107
13108 if (GTK_IS_EDITABLE(entry))
13109 {
13110 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
13111 }
13112 else if ( GTK_IS_DATA_TEXT_VIEW(entry)
13113 || GTK_IS_TEXT_VIEW(entry) )
13114 {
13115 GtkTextIter start, end;
13116 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
13117 gtk_text_buffer_get_bounds(buffer, &start, &end);
13118 text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
13119 }
13120 else
13121 {
13122 g_warning("gtk_sheet_get_entry_text: no GTK_EDITABLE, don't know how to get the text.");
13123 }
13124
13125 #if GTK_SHEET_DEBUG_SET_ENTRY_TEXT > 0
13126 g_debug("gtk_sheet_get_entry_text[%p] returns <%s>",
13127 sheet, text);
13128 #endif
13129
13130 return (text);
13131 }
13132
13133 /**
13134 * gtk_sheet_set_entry_text:
13135 * @sheet: a #GtkSheet
13136 * @text: the text to be set or NULL
13137 *
13138 * Set the text in the sheet_entry (and active cell).
13139 *
13140 * This function is mainly used to synchronize the text of a
13141 * second entry with the sheet_entry.
13142 *
13143 * This function is necessary, because not all possible entry
13144 * widgets implement the GtkEditable interface yet.
13145 */
13146 void gtk_sheet_set_entry_text(GtkSheet *sheet, const gchar *text)
13147 {
13148 GtkWidget *entry = NULL;
13149
13150 g_return_if_fail(sheet != NULL);
13151 g_return_if_fail(GTK_IS_SHEET(sheet));
13152
13153 #if GTK_SHEET_DEBUG_SET_ENTRY_TEXT > 0
13154 g_debug("gtk_sheet_set_entry_text[%p] = <%s>",
13155 sheet, text);
13156 #endif
13157
13158 if (!sheet->sheet_entry) /* PR#102114 */
13159 return;
13160
13161 entry = gtk_sheet_get_entry(sheet);
13162 g_return_if_fail(entry != NULL);
13163
13164 if (GTK_IS_EDITABLE(entry))
13165 {
13166 gint position = 0;
13167 gtk_editable_delete_text(GTK_EDITABLE(entry), 0, -1);
13168 gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &position);
13169 }
13170 else if ( GTK_IS_DATA_TEXT_VIEW(entry)
13171 || GTK_IS_TEXT_VIEW(entry) )
13172 {
13173 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
13174 GtkTextIter iter;
13175
13176 gtk_text_buffer_set_text(buffer, text, -1);
13177
13178 gtk_text_buffer_get_start_iter(buffer, &iter);
13179 gtk_text_buffer_place_cursor(buffer, &iter);
13180 }
13181 else
13182 {
13183 g_warning("gtk_sheet_set_entry_text: no GTK_EDITABLE, don't know how to set the text.");
13184 }
13185
13186 #if GTK_SHEET_DEBUG_SET_ENTRY_TEXT > 0
13187 g_debug("gtk_sheet_set_entry_text[%p] done", sheet);
13188 #endif
13189 }
13190
13191 /**
13192 * gtk_sheet_set_entry_editable:
13193 * @sheet: a #GtkSheet
13194 * @editable: editable flag
13195 *
13196 * Set the editable flag in the sheet_entry
13197 *
13198 * This function is mainly used to synchronize the editable flag
13199 * of a second entry with the sheet_entry.
13200 *
13201 * This function is necessary, because not all possible entry
13202 * widgets implement the GtkEditable interface yet.
13203 */
13204 void gtk_sheet_set_entry_editable(GtkSheet *sheet, const gboolean editable)
13205 {
13206 GtkWidget *entry = NULL;
13207
13208 g_return_if_fail(sheet != NULL);
13209 g_return_if_fail(GTK_IS_SHEET(sheet));
13210
13211 if (!sheet->sheet_entry) /* PR#102114 */
13212 return;
13213
13214 entry = gtk_sheet_get_entry(sheet);
13215 g_return_if_fail(entry != NULL);
13216
13217 if (GTK_IS_EDITABLE(entry))
13218 {
13219 gtk_editable_set_editable(GTK_EDITABLE(entry), editable);
13220 }
13221 else if ( GTK_IS_DATA_TEXT_VIEW(entry)
13222 || GTK_IS_TEXT_VIEW(entry) )
13223 {
13224 gtk_text_view_set_editable(GTK_TEXT_VIEW(entry), editable);
13225 }
13226 else
13227 {
13228 g_warning("gtk_sheet_set_entry_editable: no GTK_EDITABLE, don't know how to set editable.");
13229 }
13230 }
13231
13232 /**
13233 * gtk_sheet_entry_select_region:
13234 * @sheet: a #GtkSheet
13235 * @start_pos: start of region
13236 * @end_pos: end of region
13237 *
13238 * Selects a region of text.
13239 *
13240 * This function is necessary, because not all possible entry
13241 * widgets implement the GtkEditable interface yet.
13242 */
13243 void gtk_sheet_entry_select_region(GtkSheet *sheet, gint start_pos, gint end_pos)
13244 {
13245 GtkWidget *entry = NULL;
13246
13247 g_return_if_fail(sheet != NULL);
13248 g_return_if_fail(GTK_IS_SHEET(sheet));
13249
13250 if (!sheet->sheet_entry) /* PR#102114 */
13251 return;
13252
13253 entry = gtk_sheet_get_entry(sheet);
13254 g_return_if_fail(entry != NULL);
13255
13256 if (GTK_IS_EDITABLE(entry))
13257 {
13258 gtk_editable_select_region(GTK_EDITABLE(entry), start_pos, end_pos);
13259 }
13260 else if ( GTK_IS_DATA_TEXT_VIEW(entry)
13261 || GTK_IS_TEXT_VIEW(entry) )
13262 {
13263 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
13264 GtkTextIter start, end;
13265
13266 gtk_text_buffer_get_iter_at_offset(buffer, &start, start_pos);
13267 gtk_text_buffer_get_iter_at_offset(buffer, &end, end_pos);
13268 gtk_text_buffer_select_range(buffer, &start, &end);
13269 }
13270 else
13271 {
13272 g_warning("gtk_sheet_entry_select_region: no GTK_EDITABLE, don't know how to select region.");
13273 }
13274 }
13275
13276 /**
13277 * gtk_sheet_entry_signal_connect_changed:
13278 * @sheet: a #GtkSheet
13279 * @handler: (scope notified) the signal handler
13280 *
13281 * Connect a handler to the sheet_entry "changed" signal. The
13282 * user_data argument of the handler will be filled with the
13283 * #GtkSheet.
13284 *
13285 * This function is mainly used to synchronize a second entry
13286 * widget with the sheet_entry.
13287 *
13288 * This function is necessary, because not all possible entry
13289 * widgets implement the GtkEditable interface yet.
13290 *
13291 * Returns: the handler id
13292 */
13293 gulong gtk_sheet_entry_signal_connect_changed(GtkSheet *sheet, GCallback handler)
13294 {
13295 GtkWidget *entry = NULL;
13296 gulong handler_id = 0;
13297
13298 g_return_val_if_fail(sheet != NULL, handler_id);
13299 g_return_val_if_fail(GTK_IS_SHEET(sheet), handler_id);
13300
13301 if (!sheet->sheet_entry) /* PR#102114 */
13302 return(handler_id);
13303
13304 entry = gtk_sheet_get_entry(sheet);
13305 g_return_val_if_fail(entry != NULL, handler_id);
13306
13307 if (GTK_IS_EDITABLE(entry))
13308 {
13309 handler_id = g_signal_connect(G_OBJECT(entry),
13310 "changed", handler, GTK_OBJECT(sheet));
13311 }
13312 else if ( GTK_IS_DATA_TEXT_VIEW(entry)
13313 || GTK_IS_TEXT_VIEW(entry) )
13314 {
13315 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
13316
13317 handler_id = g_signal_connect(G_OBJECT(buffer),
13318 "changed", handler, GTK_OBJECT(sheet));
13319 }
13320 else
13321 {
13322 g_warning("gtk_sheet_entry_signal_connect_changed: no GTK_EDITABLE, don't know how to get editable.");
13323 }
13324 return (handler_id);
13325 }
13326
13327 /**
13328 * gtk_sheet_entry_signal_disconnect_by_func:
13329 * @sheet: a #GtkSheet
13330 * @handler: (scope call) the signal handler
13331 *
13332 * Disconnect a handler from the sheet_entry "changed" signal
13333 *
13334 * This function is mainly used to synchronize a second entry
13335 * widget with the sheet_entry.
13336 *
13337 * This function is necessary, because not all possible entry
13338 * widgets implement the GtkEditable interface yet.
13339 */
13340 void gtk_sheet_entry_signal_disconnect_by_func(GtkSheet *sheet, GCallback handler)
13341 {
13342 GtkWidget *entry = NULL;
13343
13344 g_return_if_fail(sheet != NULL);
13345 g_return_if_fail(GTK_IS_SHEET(sheet));
13346
13347 if (!sheet->sheet_entry) /* PR#102114 */
13348 return;
13349
13350 entry = gtk_sheet_get_entry(sheet);
13351 g_return_if_fail(entry != NULL);
13352
13353 if (GTK_IS_EDITABLE(entry))
13354 {
13355 g_signal_handlers_disconnect_by_func(G_OBJECT(entry),
13356 handler, GTK_OBJECT(sheet));
13357 }
13358 else if ( GTK_IS_DATA_TEXT_VIEW(entry)
13359 || GTK_IS_TEXT_VIEW(entry) )
13360 {
13361 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
13362
13363 g_signal_handlers_disconnect_by_func(G_OBJECT(buffer),
13364 handler, GTK_OBJECT(sheet));
13365 }
13366 else
13367 {
13368 g_warning("gtk_sheet_entry_signal_disconnect_by_func: no GTK_EDITABLE, don't know how to get editable.");
13369 }
13370 }
13371
13372
13373
13374 /* BUTTONS */
13375 static void
13376 row_button_set(GtkSheet *sheet, gint row)
13377 {
13378 if (row < 0 || row > sheet->maxrow)
13379 return;
13380 if (sheet->row[row].button.state == GTK_STATE_ACTIVE)
13381 return;
13382
13383 sheet->row[row].button.state = GTK_STATE_ACTIVE;
13384 _gtk_sheet_draw_button(sheet, row, -1);
13385 }
13386
13387 static void
13388 row_button_release(GtkSheet *sheet, gint row)
13389 {
13390 if (row < 0 || row > sheet->maxrow)
13391 return;
13392 if (sheet->row[row].button.state == GTK_STATE_NORMAL)
13393 return;
13394
13395 sheet->row[row].button.state = GTK_STATE_NORMAL;
13396 _gtk_sheet_draw_button(sheet, row, -1);
13397 }
13398
13399 /**
13400 * _gtk_sheet_draw_button:
13401 * @sheet: the #GtkSheet
13402 * @row: row index
13403 * @col: column index
13404 *
13405 * draw a sheet button
13406 * if row == -1 draw a column button
13407 * if col == -1 draw a row button
13408 * if both == -1 draw the sheet button
13409 *
13410 */
13411 void
13412 _gtk_sheet_draw_button(GtkSheet *sheet, gint row, gint col)
13413 {
13414 GdkWindow *window = NULL;
13415 GtkShadowType shadow_type;
13416 guint width = 0, height = 0;
13417 gint x = 0, y = 0;
13418 gint index = 0;
13419 guint text_width = 0, text_height = 0;
13420 GtkSheetButton *button = NULL;
13421 GtkSheetChild *child = NULL;
13422 GdkRectangle allocation;
13423 gboolean sensitive = FALSE;
13424 gint state = 0;
13425 gchar * label, label_buf[10];
13426 PangoAlignment pango_alignment = PANGO_ALIGN_LEFT;
13427 GtkSheetArea area = ON_SHEET_BUTTON_AREA;
13428 PangoFontDescription *font_desc =
13429 gtk_widget_get_style(GTK_WIDGET(sheet))->font_desc;
13430 PangoRectangle extent;
13431
13432 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
13433 return;
13434
13435 if ((row == -1) && (col == -1))
13436 return;
13437
13438 #if GTK_SHEET_DEBUG_DRAW_BUTTON > 0
13439 g_debug("_gtk_sheet_draw_button: row %d col %d", row, col);
13440 #endif
13441
13442 if (row >= 0)
13443 {
13444 if (row > sheet->maxrow)
13445 return;
13446 if (!sheet->row_titles_visible)
13447 return;
13448 if (!GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, row)))
13449 return;
13450 if (row < MIN_VIEW_ROW(sheet))
13451 return;
13452 if (row > MAX_VIEW_ROW(sheet))
13453 return;
13454 }
13455
13456 if (col >= 0)
13457 {
13458 if (col > sheet->maxcol)
13459 return;
13460 if (!sheet->column_titles_visible)
13461 return;
13462 if (!GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, col)))
13463 return;
13464 if (col < MIN_VIEW_COLUMN(sheet))
13465 return;
13466 if (col > MAX_VIEW_COLUMN(sheet))
13467 return;
13468 }
13469
13470 if (row == -1)
13471 {
13472 window = sheet->column_title_window;
13473 button = &COLPTR(sheet, col)->button;
13474 index = col;
13475 x = _gtk_sheet_column_left_xpixel(sheet, col) + CELL_SPACING;
13476 if (sheet->row_titles_visible)
13477 x -= sheet->row_title_area.width;
13478 y = 0;
13479 width = COLPTR(sheet, col)->width;
13480 height = sheet->column_title_area.height;
13481 sensitive = GTK_SHEET_COLUMN_IS_SENSITIVE(COLPTR(sheet, col));
13482 area = ON_COLUMN_TITLES_AREA;
13483 }
13484 else if (col == -1)
13485 {
13486 window = sheet->row_title_window;
13487 button = &sheet->row[row].button;
13488 index = row;
13489 x = 0;
13490 y = _gtk_sheet_row_top_ypixel(sheet, row) + CELL_SPACING;
13491 if (sheet->column_titles_visible)
13492 y -= sheet->column_title_area.height;
13493 width = sheet->row_title_area.width;
13494 height = sheet->row[row].height;
13495 sensitive = GTK_SHEET_ROW_IS_SENSITIVE(ROWPTR(sheet, row));
13496 area = ON_ROW_TITLES_AREA;
13497 }
13498
13499 allocation.x = x;
13500 allocation.y = y;
13501 allocation.width = width;
13502 allocation.height = height;
13503
13504 gdk_window_clear_area(window, x, y, width, height);
13505
13506 state = button->state;
13507 if (!sensitive)
13508 state = GTK_STATE_INSENSITIVE;
13509
13510 if (state == GTK_STATE_ACTIVE)
13511 shadow_type = GTK_SHADOW_IN;
13512 else
13513 shadow_type = GTK_SHADOW_OUT;
13514
13515 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
13516 gtk_paint_box(gtk_widget_get_style(sheet->button), window,
13517 button->state, shadow_type,
13518 &allocation, GTK_WIDGET(sheet->button),
13519 "table-heading", x, y, width, height);
13520 else
13521 gtk_paint_box(gtk_widget_get_style(sheet->button), window,
13522 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
13523 &allocation, GTK_WIDGET(sheet->button),
13524 "table-heading", x, y, width, height);
13525
13526 if (button->label_visible)
13527 {
13528 PangoLayout *layout = NULL;
13529 gint real_x = x, real_y;
13530
13531 text_height =
13532 _gtk_sheet_row_default_height(GTK_WIDGET(sheet)) - 2 * CELLOFFSET;
13533
13534 gdk_gc_set_clip_rectangle(
13535 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[button->state],
13536 &allocation);
13537 gdk_gc_set_clip_rectangle(
13538 gtk_widget_get_style(GTK_WIDGET(sheet))->white_gc,
13539 &allocation);
13540
13541 y += 2 * gtk_widget_get_style(sheet->button)->ythickness;
13542
13543 real_y = y;
13544 label = button->label;
13545
13546 if (!label || !label[0]) /* revert to auto-generated numeric label */
13547 {
13548 sprintf(label_buf, "%d", index);
13549 label = label_buf;
13550 }
13551
13552 layout = gtk_widget_create_pango_layout(GTK_WIDGET(sheet), label);
13553 pango_layout_set_font_description(layout, font_desc);
13554
13555 pango_layout_get_pixel_extents(layout, NULL, &extent);
13556 text_width = extent.width;
13557
13558 switch(button->justification)
13559 {
13560 case GTK_JUSTIFY_LEFT:
13561 real_x = x + CELLOFFSET;
13562 pango_alignment = PANGO_ALIGN_LEFT;
13563 break;
13564
13565 case GTK_JUSTIFY_RIGHT:
13566 real_x = x + width - text_width - CELLOFFSET;
13567 pango_alignment = PANGO_ALIGN_RIGHT;
13568 break;
13569
13570 case GTK_JUSTIFY_FILL:
13571 pango_layout_set_justify(layout, TRUE);
13572 /* FALLTHROUGH */
13573
13574 case GTK_JUSTIFY_CENTER:
13575 real_x = x + (width - text_width) / 2;
13576 pango_alignment = PANGO_ALIGN_CENTER;
13577
13578 default:
13579 break;
13580 }
13581 pango_layout_set_alignment(layout, pango_alignment);
13582 gtk_paint_layout(gtk_widget_get_style(GTK_WIDGET(sheet)),
13583 window,
13584 state,
13585 FALSE,
13586 &allocation,
13587 GTK_WIDGET(sheet),
13588 "label",
13589 real_x, real_y,
13590 layout);
13591 g_object_unref(G_OBJECT(layout));
13592
13593 gdk_gc_set_clip_rectangle(
13594 gtk_widget_get_style(GTK_WIDGET(sheet))->fg_gc[button->state],
13595 NULL);
13596 gdk_gc_set_clip_rectangle(
13597 gtk_widget_get_style(GTK_WIDGET(sheet))->white_gc,
13598 NULL);
13599 }
13600
13601 gtk_sheet_draw_tooltip_marker(sheet, area, row, col);
13602
13603 if ((child = button->child) && (child->widget))
13604 {
13605 GtkRequisition requisition;
13606
13607 child->x = allocation.x;
13608 child->y = allocation.y;
13609
13610 gtk_widget_get_requisition(child->widget, &requisition);
13611
13612 child->x += (width - requisition.width) / 2;
13613 child->y += (height - requisition.height) / 2;
13614
13615 allocation.x = child->x;
13616 allocation.y = child->y;
13617 allocation.width = requisition.width;
13618 allocation.height = requisition.height;
13619
13620 x = child->x;
13621 y = child->y;
13622
13623 gtk_widget_set_state(child->widget, button->state);
13624
13625 if (gtk_widget_get_realized(GTK_WIDGET(sheet)) &&
13626 gtk_widget_get_mapped(child->widget))
13627 {
13628 gtk_widget_size_allocate(child->widget, &allocation);
13629 gtk_widget_queue_draw(child->widget);
13630 }
13631 }
13632 }
13633
13634
13635 /* SCROLLBARS
13636 *
13637 * functions:
13638 * adjust_scrollbars
13639 * vadjustment_changed
13640 * hadjustment_changed
13641 * vadjustment_value_changed
13642 * hadjustment_value_changed */
13643
13644 /**
13645 * _gtk_sheet_scrollbar_adjust:
13646 *
13647 * @param sheet the #GtkSheet
13648 *
13649 * recalculate scrollbar adjustments and emit changed signals
13650 */
13651 void
13652 _gtk_sheet_scrollbar_adjust(GtkSheet *sheet)
13653 {
13654 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13655 g_debug("_gtk_sheet_scrollbar_adjust: called");
13656 #endif
13657
13658 if (sheet->vadjustment)
13659 {
13660 GtkAdjustment *va = sheet->vadjustment;
13661
13662 gint upper = gtk_sheet_height(sheet) + 80;
13663 gint page_size = sheet->sheet_window_height;
13664
13665 gtk_adjustment_configure(va,
13666 gtk_adjustment_get_value(va), /* value */
13667 0.0, /* lower */
13668 upper, /* upper */
13669 _gtk_sheet_row_default_height(GTK_WIDGET(sheet)), /* step_increment */
13670 sheet->sheet_window_height / 2, /* page_increment */
13671 page_size /* page_size */
13672 );
13673
13674 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13675 g_debug("_gtk_sheet_scrollbar_adjust: va PS %d PI %g SI %g L %g U %d V %g VO %d",
13676 page_size,
13677 gtk_adjustment_get_page_increment(va),
13678 gtk_adjustment_get_step_increment(va),
13679 gtk_adjustment_get_lower(va),
13680 upper,
13681 gtk_adjustment_get_value(va),
13682 sheet->voffset);
13683 #endif
13684
13685 if (upper <= page_size) /* whole sheet fits into window? */
13686 {
13687 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13688 g_debug("_gtk_sheet_scrollbar_adjust: reset V to 0");
13689 #endif
13690 gtk_adjustment_set_value(va, 0);
13691 gtk_adjustment_value_changed(va);
13692 }
13693 /* can produce smudge effects - 23.3.13/fp
13694 else if (va->value >= va->upper - va->page_size)
13695 {
13696 va->value = va->upper - va->page_size;
13697 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13698 g_debug("_gtk_sheet_scrollbar_adjust: reset to V %g VO %d",
13699 va->value, sheet->voffset);
13700 #endif
13701 g_signal_emit_by_name(GTK_OBJECT(va), "value_changed");
13702 }
13703 */
13704
13705 gtk_adjustment_changed(va);
13706 }
13707
13708 if (sheet->hadjustment)
13709 {
13710 GtkAdjustment *ha = sheet->hadjustment;
13711
13712 /* gint upper = gtk_sheet_width(sheet) * 3 / 2; */
13713 gint upper = gtk_sheet_width(sheet) + 80;
13714 gint page_size = sheet->sheet_window_width;
13715
13716 gtk_adjustment_configure(ha,
13717 gtk_adjustment_get_value(ha), /* value */
13718 0.0, /* lower */
13719 upper, /* upper */
13720 GTK_SHEET_COLUMN_DEFAULT_WIDTH, /* step_increment */
13721 sheet->sheet_window_width / 2, /* page_increment */
13722 page_size /* page_size */
13723 );
13724
13725 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13726 g_debug("_gtk_sheet_scrollbar_adjust: ha PS %d PI %g SI %g L %g U %d V %g HO %d",
13727 page_size,
13728 gtk_adjustment_get_page_increment(ha),
13729 gtk_adjustment_get_step_increment(ha),
13730 gtk_adjustment_get_lower(ha),
13731 upper,
13732 gtk_adjustment_get_value(ha), sheet->hoffset);
13733 #endif
13734
13735 if (upper <= page_size) /* whole sheet fits into window? */
13736 {
13737 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13738 g_debug("_gtk_sheet_scrollbar_adjust: reset V to 0");
13739 #endif
13740 gtk_adjustment_set_value(ha, 0);
13741 gtk_adjustment_value_changed(ha);
13742 }
13743 /* can produce smudge effects - 23.3.13/fp
13744 else if (ha->value >= ha->upper - ha->page_size)
13745 {
13746 ha->value = ha->upper - ha->page_size;
13747 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13748 g_debug("_gtk_sheet_scrollbar_adjust: reset to V %g HO %d",
13749 va->value, sheet->hoffset);
13750 #endif
13751 g_signal_emit_by_name(GTK_OBJECT(ha), "value_changed");
13752 }
13753 */
13754
13755 gtk_adjustment_changed(ha);
13756 }
13757 }
13758
13759
13760 /*
13761 * _vadjustment_changed_handler:
13762 *
13763 * this is the #GtkSheet vertical adjustment "changed" signal handler
13764 *
13765 * @param adjustment the #GtkAdjustment that received the signal
13766 * @param data the #GtkSheet passed on signal creation
13767 */
13768 static void
13769 _vadjustment_changed_handler(GtkAdjustment *adjustment,
13770 gpointer data)
13771 {
13772 GtkSheet *sheet;
13773
13774 g_return_if_fail(adjustment != NULL);
13775 g_return_if_fail(data != NULL);
13776
13777 sheet = GTK_SHEET(data);
13778
13779 #if GTK_SHEET_DEBUG_ADJUSTMENT > 1
13780 g_debug("_vadjustment_changed_handler: called: O VA %g VO %d",
13781 sheet->old_vadjustment, sheet->voffset);
13782 #endif
13783 }
13784
13785 /*
13786 * _hadjustment_changed_handler:
13787 *
13788 * this is the #GtkSheet horizontal adjustment "change" handler
13789 *
13790 * @param adjustment the #GtkAdjustment that received the signal
13791 * @param data the #GtkSheet passed on signal creation
13792 */
13793 static void
13794 _hadjustment_changed_handler(GtkAdjustment *adjustment, gpointer data)
13795 {
13796 GtkSheet *sheet;
13797
13798 g_return_if_fail(adjustment != NULL);
13799 g_return_if_fail(data != NULL);
13800
13801 sheet = GTK_SHEET(data);
13802
13803 #if GTK_SHEET_DEBUG_ADJUSTMENT > 1
13804 g_debug("_hadjustment_changed_handler: called: O VA %g VO %d",
13805 sheet->old_vadjustment, sheet->voffset);
13806 #endif
13807 }
13808
13809
13810 /*
13811 * vadjustment_value_changed_handler:
13812 *
13813 * this is the #GtkSheet vertical adjustment "value-changed" signal handler
13814 *
13815 * @param adjustment the #GtkAdjustment that received the signal
13816 * @param data the #GtkSheet passed on signal creation
13817 */
13818 static void
13819 _vadjustment_value_changed_handler(GtkAdjustment *adjustment, gpointer data)
13820 {
13821 GtkSheet *sheet;
13822 gint old_value;
13823
13824 g_return_if_fail(adjustment != NULL);
13825 g_return_if_fail(data != NULL);
13826 g_return_if_fail(GTK_IS_SHEET(data));
13827
13828 sheet = GTK_SHEET(data);
13829
13830 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13831 g_debug("_vadjustment_value_changed_handler: called: O VA %g VO %d ar %d ac %d",
13832 sheet->old_vadjustment, sheet->voffset,
13833 sheet->active_cell.row, sheet->active_cell.col);
13834 #endif
13835
13836 if (GTK_SHEET_IS_FROZEN(sheet))
13837 return;
13838
13839 #if 0
13840 if (sheet->column_titles_visible)
13841 row = _gtk_sheet_row_from_ypixel(sheet, sheet->column_title_area.height + CELL_SPACING);
13842 else
13843 row = _gtk_sheet_row_from_ypixel(sheet, CELL_SPACING);
13844
13845 old_value = -sheet->voffset;
13846
13847 for (i = 0; i < sheet->maxrow; i++) /* all but the last row */
13848 {
13849 if (GTK_SHEET_ROW_IS_VISIBLE(ROWPTR(sheet, i)))
13850 y += sheet->row[i].height;
13851 if (y > gtk_adjustment_get_value(adjustment))
13852 break;
13853 }
13854 if (0 <= i && i <= sheet->maxrow)
13855 y -= sheet->row[i].height;
13856 new_row = i;
13857
13858 y = MAX(y, 0);
13859
13860 #if 0
13861 if (adjustment->value > sheet->old_vadjustment && sheet->old_vadjustment > 0. &&
13862 0 <= new_row && new_row <= sheet->maxrow &&
13863 sheet->row[new_row].height > sheet->vadjustment->step_increment)
13864 {
13865 /* This avoids embarrassing twitching */
13866 if (row == new_row && row != sheet->maxrow &&
13867 adjustment->value - sheet->old_vadjustment >=
13868 sheet->vadjustment->step_increment &&
13869 new_row + 1 != MIN_VISIBLE_ROW(sheet))
13870 {
13871 new_row+=1;
13872 y=y+sheet->row[row].height;
13873 }
13874 }
13875 #else
13876 if (gtk_adjustment_get_value(adjustment) > sheet->old_vadjustment && sheet->old_vadjustment > 0. &&
13877 0 <= new_row && new_row <= sheet->maxrow &&
13878 sheet->row[new_row].height > gtk_adjustment_get_step_increment(sheet->vadjustment))
13879 {
13880 new_row += 1;
13881 y = y + sheet->row[row].height;
13882 }
13883 #endif
13884
13885 /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
13886 if (sheet->old_vadjustment >= 0. && row == new_row)
13887 {
13888 sheet->old_vadjustment = gtk_adjustment_get_value(sheet->vadjustment);
13889
13890 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13891 g_debug("_vadjustment_value_changed_handler: return 1: vv %g",
13892 sheet->old_vadjustment);
13893 #endif
13894 return;
13895 }
13896
13897 sheet->old_vadjustment = gtk_adjustment_get_value(sheet->vadjustment);
13898 gtk_adjustment_set_value(adjustment, y);
13899
13900 if (new_row < 0 || new_row > sheet->maxrow)
13901 {
13902 gtk_adjustment_set_step_increment(sheet->vadjustment,
13903 GTK_SHEET_ROW_DEFAULT_HEIGHT);
13904 }
13905 else if (new_row == 0)
13906 {
13907 gtk_adjustment_set_step_increment(sheet->vadjustment,
13908 sheet->row[0].height);
13909 }
13910 else
13911 {
13912 gtk_adjustment_set_step_increment(sheet->vadjustment,
13913 MIN(sheet->row[new_row].height, sheet->row[new_row - 1].height));
13914 }
13915
13916 value = gtk_adjustment_get_value(adjustment);
13917 gtk_adjustment_set_value(sheet->vadjustment, value);
13918
13919 if (value >= -sheet->voffset)
13920 {
13921 /* scroll down */
13922 diff = value + sheet->voffset;
13923 }
13924 else
13925 {
13926 /* scroll up */
13927 diff = -sheet->voffset - value;
13928 }
13929
13930 sheet->voffset = -value;
13931 #else
13932 /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
13933 old_value = sheet->old_vadjustment;
13934 sheet->old_vadjustment = gtk_adjustment_get_value(sheet->vadjustment);
13935
13936 if (old_value >= 0. && sheet->voffset == -1 * gtk_adjustment_get_value(adjustment))
13937 return;
13938
13939 gdouble value = gtk_adjustment_get_value(adjustment);
13940 gtk_adjustment_set_value(sheet->vadjustment, value);
13941 sheet->voffset = -value;
13942 #endif
13943
13944 _gtk_sheet_recalc_view_range(sheet);
13945
13946 if (gtk_widget_get_realized(sheet->sheet_entry) &&
13947 sheet->state == GTK_SHEET_NORMAL &&
13948 sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
13949 !gtk_sheet_cell_isvisible(sheet, sheet->active_cell.row, sheet->active_cell.col))
13950 {
13951 /* PR#211566 - removed gtk_sheet_cell_clear() */
13952 gtk_widget_unmap(sheet->sheet_entry);
13953 }
13954
13955
13956 gtk_sheet_position_children(sheet);
13957
13958 size_allocate_global_button(sheet);
13959 size_allocate_row_title_buttons(sheet);
13960
13961 _gtk_sheet_range_draw(sheet, NULL, TRUE);
13962 }
13963
13964 /*
13965 * hadjustment_value_changed_handler:
13966 *
13967 * this is the #GtkSheet horizontal adjustment "value-changed" handler
13968 *
13969 * @param adjustment the #GtkAdjustment that received the signal
13970 * @param data the #GtkSheet passed on signal creation
13971 */
13972 static void
13973 _hadjustment_value_changed_handler(GtkAdjustment *adjustment, gpointer data)
13974 {
13975 GtkSheet *sheet;
13976 gint old_value;
13977
13978 g_return_if_fail(adjustment != NULL);
13979 g_return_if_fail(data != NULL);
13980 g_return_if_fail(GTK_IS_SHEET(data));
13981
13982 sheet = GTK_SHEET(data);
13983
13984 #if GTK_SHEET_DEBUG_ADJUSTMENT > 0
13985 g_debug("_hadjustment_value_changed_handler: called: O HA %g HO %d",
13986 sheet->old_hadjustment, sheet->hoffset);
13987 #endif
13988
13989 if (GTK_SHEET_IS_FROZEN(sheet))
13990 return;
13991
13992 #if 0
13993 if (sheet->row_titles_visible) col = _gtk_sheet_column_from_xpixel(sheet, sheet->row_title_area.width + CELL_SPACING);
13994 else
13995 col = _gtk_sheet_column_from_xpixel(sheet, CELL_SPACING);
13996
13997 old_value = -sheet->hoffset;
13998
13999 for (i = 0; i < sheet->maxcol; i++) /* all but the last column */
14000 {
14001 if (GTK_SHEET_COLUMN_IS_VISIBLE(COLPTR(sheet, i))) x += COLPTR(sheet, i)->width;
14002 if (x > adjustment->value) break;
14003 }
14004 if (0 <= i && i <= sheet->maxcol) x -= COLPTR(sheet, i)->width;
14005 new_col = i;
14006
14007 x = MAX(x, 0);
14008
14009 #if 0
14010 if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 &&
14011 0 <= new_col && new_col <= sheet->maxcol &&
14012 COLPTR(sheet, new_col)->width > sheet->hadjustment->step_increment)
14013 {
14014 /* This avoids embarrassing twitching */
14015 if (col == new_col && col != sheet->maxcol &&
14016 adjustment->value - sheet->old_hadjustment >=
14017 sheet->hadjustment->step_increment &&
14018 new_col + 1 != MIN_VISIBLE_COLUMN(sheet))
14019 {
14020 new_col+=1;
14021 x=x+COLPTR(sheet, col)->width;
14022 }
14023 }
14024 #else
14025 if (adjustment->value > sheet->old_hadjustment && sheet->old_hadjustment > 0 &&
14026 0 <= new_col && new_col <= sheet->maxcol &&
14027 COLPTR(sheet, new_col)->width > sheet->hadjustment->step_increment)
14028 {
14029 new_col += 1;
14030 x = x + COLPTR(sheet, col)->width;
14031 }
14032 #endif
14033
14034 /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
14035 if (sheet->old_hadjustment >= 0. && new_col == col)
14036 {
14037 sheet->old_hadjustment = sheet->hadjustment->value;
14038 return;
14039 }
14040
14041 sheet->old_hadjustment = sheet->hadjustment->value;
14042 adjustment->value = x;
14043
14044 if (new_col < 0 || new_col > sheet->maxcol)
14045 {
14046 sheet->hadjustment->step_increment = GTK_SHEET_COLUMN_DEFAULT_WIDTH;
14047 }
14048 else if (new_col == 0)
14049 {
14050 sheet->hadjustment->step_increment = COLPTR(sheet, 0)->width;
14051 }
14052 else
14053 {
14054 sheet->hadjustment->step_increment =
14055 MIN(COLPTR(sheet, new_col)->width, COLPTR(sheet, new_col - 1)->width);
14056 }
14057
14058 sheet->hadjustment->value = adjustment->value;
14059 value = adjustment->value;
14060
14061 if (value >= -sheet->hoffset)
14062 {
14063 /* scroll right */
14064 diff = value + sheet->hoffset;
14065 }
14066 else
14067 {
14068 /* scroll left */
14069 diff = -sheet->hoffset - value;
14070 }
14071
14072 sheet->hoffset = -value;
14073 #else
14074 /* Negative old_adjustment enforces the redraw, otherwise avoid spureous redraw */
14075 old_value = sheet->old_hadjustment;
14076 sheet->old_hadjustment = gtk_adjustment_get_value(sheet->hadjustment);
14077
14078 if (old_value >= 0. && sheet->hoffset == -1 * gtk_adjustment_get_value(adjustment))
14079 return;
14080
14081 gdouble value = gtk_adjustment_get_value(adjustment);
14082 gtk_adjustment_set_value(sheet->hadjustment, value);
14083 sheet->hoffset = -value;
14084 #endif
14085
14086 _gtk_sheet_recalc_view_range(sheet);
14087
14088 if (gtk_widget_get_realized(sheet->sheet_entry) &&
14089 sheet->state == GTK_SHEET_NORMAL &&
14090 sheet->active_cell.row >= 0 && sheet->active_cell.col >= 0 &&
14091 !gtk_sheet_cell_isvisible(sheet, sheet->active_cell.row, sheet->active_cell.col))
14092 {
14093 /* PR#211566 - removed gtk_sheet_cell_clear() */
14094 gtk_widget_unmap(sheet->sheet_entry);
14095 }
14096
14097 gtk_sheet_position_children(sheet);
14098
14099 size_allocate_global_button(sheet);
14100 _gtk_sheet_column_buttons_size_allocate(sheet);
14101
14102 _gtk_sheet_range_draw(sheet, NULL, TRUE);
14103 }
14104
14105
14106 /* COLUMN RESIZING */
14107 static void
14108 draw_xor_vline(GtkSheet *sheet)
14109 {
14110 GtkWidget *widget;
14111
14112 g_return_if_fail(sheet != NULL);
14113
14114 widget = GTK_WIDGET(sheet);
14115
14116 gdk_draw_line(gtk_widget_get_window(widget), sheet->xor_gc,
14117 sheet->x_drag,
14118 sheet->column_title_area.height,
14119 sheet->x_drag,
14120 sheet->sheet_window_height + 1);
14121 }
14122
14123 /* ROW RESIZING */
14124 static void
14125 draw_xor_hline(GtkSheet *sheet)
14126 {
14127 GtkWidget *widget;
14128
14129 g_return_if_fail(sheet != NULL);
14130
14131 widget = GTK_WIDGET(sheet);
14132
14133 gdk_draw_line(gtk_widget_get_window(widget), sheet->xor_gc,
14134 sheet->row_title_area.width,
14135 sheet->y_drag,
14136 sheet->sheet_window_width + 1,
14137 sheet->y_drag);
14138 }
14139
14140 /* SELECTED RANGE */
14141 static void
14142 draw_xor_rectangle(GtkSheet *sheet, GtkSheetRange range)
14143 {
14144 gint i;
14145 GdkRectangle clip_area, area;
14146 GdkGCValues values;
14147
14148 if (range.col0 < 0 || range.coli < 0 || range.row0 < 0 || range.rowi < 0)
14149 return; /* PR#203012 */
14150
14151 area.x = _gtk_sheet_column_left_xpixel(sheet, range.col0);
14152 area.y = _gtk_sheet_row_top_ypixel(sheet, range.row0);
14153 area.width = _gtk_sheet_column_left_xpixel(sheet, range.coli) - area.x +
14154 COLPTR(sheet, range.coli)->width;
14155 area.height = _gtk_sheet_row_top_ypixel(sheet, range.rowi) - area.y +
14156 sheet->row[range.rowi].height;
14157
14158 clip_area.x = sheet->row_title_area.width;
14159 clip_area.y = sheet->column_title_area.height;
14160 clip_area.width = sheet->sheet_window_width;
14161 clip_area.height = sheet->sheet_window_height;
14162
14163 if (!sheet->row_titles_visible)
14164 clip_area.x = 0;
14165 if (!sheet->column_titles_visible)
14166 clip_area.y = 0;
14167
14168 if (area.x < 0)
14169 {
14170 area.width = area.width + area.x;
14171 area.x = 0;
14172 }
14173 if (area.width > clip_area.width)
14174 area.width = clip_area.width + 10;
14175 if (area.y < 0)
14176 {
14177 area.height = area.height + area.y;
14178 area.y = 0;
14179 }
14180 if (area.height > clip_area.height)
14181 area.height = clip_area.height + 10;
14182
14183 clip_area.x--;
14184 clip_area.y--;
14185 clip_area.width += 3;
14186 clip_area.height += 3;
14187
14188 gdk_gc_get_values(sheet->xor_gc, &values);
14189
14190 gdk_gc_set_clip_rectangle(sheet->xor_gc, &clip_area);
14191
14192 for (i = -1; i <= 1; ++i) gdk_draw_rectangle(sheet->sheet_window,
14193 sheet->xor_gc,
14194 FALSE,
14195 area.x + i, area.y + i,
14196 area.width - 2 * i, area.height - 2 * i);
14197
14198
14199 gdk_gc_set_clip_rectangle(sheet->xor_gc, NULL);
14200
14201 gdk_gc_set_foreground(sheet->xor_gc, &values.foreground);
14202 }
14203
14204
14205 /* this function returns the new width of the column being resized given
14206 * the column and x position of the cursor; the x cursor position is passed
14207 * in as a pointer and automaticaly corrected if it's beyond min/max limits */
14208
14209 static guint
14210 new_column_width(GtkSheet *sheet, gint col, gint *x)
14211 {
14212 gint cx, width;
14213 GtkRequisition requisition;
14214
14215 #if GTK_SHEET_DEBUG_SIZE > 0
14216 g_debug("new_column_width: called");
14217 #endif
14218
14219 cx = *x;
14220
14221 requisition.width = COLPTR(sheet, col)->requisition;
14222
14223 /* you can't shrink a column to less than its minimum width */
14224 if (cx < _gtk_sheet_column_left_xpixel(sheet, col) + requisition.width)
14225 {
14226 *x = cx = _gtk_sheet_column_left_xpixel(sheet, col) + requisition.width;
14227 }
14228
14229 /* don't grow past the end of the window */
14230 /*
14231 if (cx > sheet->sheet_window_width)
14232 {
14233 *x = cx = sheet->sheet_window_width;
14234 }
14235 */
14236
14237 /* calculate new column width making sure it doesn't end up
14238 * less than the minimum width */
14239 width = cx - _gtk_sheet_column_left_xpixel(sheet, col);
14240 if (width < requisition.width)
14241 width = requisition.width;
14242
14243 COLPTR(sheet, col)->width = width;
14244 _gtk_sheet_recalc_left_xpixels(sheet);
14245 _gtk_sheet_recalc_view_range(sheet);
14246
14247 _gtk_sheet_column_buttons_size_allocate(sheet);
14248
14249 return (width);
14250 }
14251
14252 /* this function returns the new height of the row being resized given
14253 * the row and y position of the cursor; the y cursor position is passed
14254 * in as a pointer and automaticaly corrected if it's beyond min/max limits */
14255
14256 static guint
14257 new_row_height(GtkSheet *sheet, gint row, gint *y)
14258 {
14259 GtkRequisition requisition;
14260 gint cy, height;
14261
14262 cy = *y;
14263
14264 requisition.height = sheet->row[row].requisition;
14265
14266 /* you can't shrink a row to less than its minimum height */
14267 if (cy < _gtk_sheet_row_top_ypixel(sheet, row) + requisition.height)
14268 {
14269 *y = cy = _gtk_sheet_row_top_ypixel(sheet, row) + requisition.height;
14270 }
14271
14272 /* don't grow past the end of the window */
14273 /*
14274 if (cy > sheet->sheet_window_height)
14275 {
14276 *y = cy = sheet->sheet_window_height;
14277 }
14278 */
14279
14280 /* calculate new row height making sure it doesn't end up
14281 * less than the minimum height */
14282 height = (cy - _gtk_sheet_row_top_ypixel(sheet, row));
14283 if (height < requisition.height)
14284 height = requisition.height;
14285
14286 sheet->row[row].height = height;
14287 _gtk_sheet_recalc_top_ypixels(sheet);
14288 _gtk_sheet_recalc_view_range(sheet);
14289
14290 size_allocate_row_title_buttons(sheet);
14291
14292 return (height);
14293 }
14294
14295
14296 /**
14297 * gtk_sheet_set_row_height:
14298 * @sheet: a #GtkSheet.
14299 * @row: row number.
14300 * @height: row height(in pixels).
14301 *
14302 * Set row height.
14303 */
14304 void
14305 gtk_sheet_set_row_height(GtkSheet *sheet, gint row, guint height)
14306 {
14307 guint min_height;
14308
14309 g_return_if_fail(sheet != NULL);
14310 g_return_if_fail(GTK_IS_SHEET(sheet));
14311
14312 if (row < 0 || row > sheet->maxrow)
14313 return;
14314
14315 gtk_sheet_row_size_request(sheet, row, &min_height);
14316
14317 if (height < min_height) height = min_height;
14318
14319 sheet->row[row].height = height;
14320
14321 _gtk_sheet_recalc_top_ypixels(sheet);
14322
14323 if (gtk_widget_get_realized(GTK_WIDGET(sheet))
14324 && !GTK_SHEET_IS_FROZEN(sheet))
14325 {
14326 size_allocate_row_title_buttons(sheet);
14327 _gtk_sheet_scrollbar_adjust(sheet);
14328 _gtk_sheet_entry_size_allocate(sheet);
14329 _gtk_sheet_range_draw(sheet, NULL, TRUE);
14330 }
14331
14332 g_signal_emit(GTK_OBJECT(sheet),
14333 sheet_signals[NEW_ROW_HEIGHT], 0, row, height);
14334 }
14335
14336 /**
14337 * gtk_sheet_add_column:
14338 * @sheet: a #GtkSheet.
14339 * @ncols: number of columns to be appended.
14340 *
14341 * Append @ncols columns to the right of the sheet.
14342 */
14343 void
14344 gtk_sheet_add_column(GtkSheet *sheet, guint ncols)
14345 {
14346
14347 g_return_if_fail(sheet != NULL);
14348 g_return_if_fail(GTK_IS_SHEET(sheet));
14349
14350 AddColumns(sheet, sheet->maxcol + 1, ncols);
14351
14352 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
14353 return;
14354
14355 _gtk_sheet_scrollbar_adjust(sheet);
14356
14357 if (sheet->state == GTK_SHEET_ROW_SELECTED)
14358 sheet->range.coli += ncols;
14359
14360 _gtk_sheet_redraw_internal(sheet, TRUE, FALSE);
14361 }
14362
14363 /**
14364 * gtk_sheet_add_row:
14365 * @sheet: a #GtkSheet.
14366 * @nrows: number of rows to be appended.
14367 *
14368 * Append @nrows rows to the end of the sheet.
14369 */
14370 void
14371 gtk_sheet_add_row(GtkSheet *sheet, guint nrows)
14372 {
14373 g_return_if_fail(sheet != NULL);
14374 g_return_if_fail(GTK_IS_SHEET(sheet));
14375
14376 AddRows(sheet, sheet->maxrow + 1, nrows);
14377
14378 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
14379 return;
14380
14381 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
14382 sheet->range.rowi += nrows;
14383
14384 _gtk_sheet_scrollbar_adjust(sheet);
14385
14386 _gtk_sheet_redraw_internal(sheet, FALSE, TRUE);
14387 }
14388
14389 /**
14390 * gtk_sheet_insert_rows:
14391 * @sheet: a #GtkSheet.
14392 * @row: row number.
14393 * @nrows: number of rows to be inserted.
14394 *
14395 * Insert @nrows rows before the given row and pull right.
14396 */
14397 void
14398 gtk_sheet_insert_rows(GtkSheet *sheet, guint row, guint nrows)
14399 {
14400 GList *children;
14401 GtkSheetChild *child;
14402
14403 g_return_if_fail(sheet != NULL);
14404 g_return_if_fail(GTK_IS_SHEET(sheet));
14405
14406 gtk_sheet_real_unselect_range(sheet, NULL);
14407
14408 InsertRow(sheet, row, nrows);
14409
14410 children = sheet->children;
14411 while (children)
14412 {
14413 child = (GtkSheetChild *)children->data;
14414
14415 if (child->attached_to_cell)
14416 if (child->row >= row)
14417 child->row += nrows;
14418
14419 children = children->next;
14420 }
14421
14422 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
14423 return;
14424
14425 if (sheet->state == GTK_SHEET_COLUMN_SELECTED)
14426 sheet->range.rowi += nrows;
14427
14428 _gtk_sheet_scrollbar_adjust(sheet);
14429 _gtk_sheet_redraw_internal(sheet, FALSE, TRUE);
14430 }
14431
14432 /**
14433 * gtk_sheet_insert_columns:
14434 * @sheet: a #GtkSheet.
14435 * @col: column number.
14436 * @ncols: number of columns to be inserted.
14437 *
14438 * Insert @ncols columns before the given row and pull right.
14439 */
14440 void
14441 gtk_sheet_insert_columns(GtkSheet *sheet, guint col, guint ncols)
14442 {
14443 GList *children;
14444 GtkSheetChild *child;
14445
14446 g_return_if_fail(sheet != NULL);
14447 g_return_if_fail(GTK_IS_SHEET(sheet));
14448
14449 gtk_sheet_real_unselect_range(sheet, NULL);
14450
14451 InsertColumn(sheet, col, ncols);
14452
14453 children = sheet->children;
14454 while (children)
14455 {
14456 child = (GtkSheetChild *)children->data;
14457
14458 if (child->attached_to_cell)
14459 if (child->col >= col)
14460 child->col += ncols;
14461
14462 children = children->next;
14463 }
14464
14465 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
14466 return;
14467
14468 if (sheet->state == GTK_SHEET_ROW_SELECTED)
14469 sheet->range.coli += ncols;
14470
14471 _gtk_sheet_scrollbar_adjust(sheet);
14472 _gtk_sheet_redraw_internal(sheet, TRUE, FALSE);
14473 }
14474
14475 /**
14476 * gtk_sheet_delete_rows:
14477 * @sheet: a #GtkSheet.
14478 * @row: row number.
14479 * @nrows: number of rows to be deleted.
14480 *
14481 * Delete @nrows rows starting from @row.
14482 */
14483 void
14484 gtk_sheet_delete_rows(GtkSheet *sheet, guint row, guint nrows)
14485 {
14486 GList *children;
14487 GtkSheetChild *child;
14488 gint act_row, act_col;
14489
14490 g_return_if_fail(sheet != NULL);
14491 g_return_if_fail(GTK_IS_SHEET(sheet));
14492
14493 nrows = MIN(nrows, sheet->maxrow - row + 1);
14494
14495 act_row = sheet->active_cell.row;
14496 act_col = sheet->active_cell.col;
14497
14498 _gtk_sheet_hide_active_cell(sheet);
14499 gtk_sheet_real_unselect_range(sheet, NULL);
14500
14501 DeleteRow(sheet, row, nrows);
14502
14503 children = sheet->children;
14504 while (children)
14505 {
14506 child = (GtkSheetChild *)children->data;
14507
14508 if (child->attached_to_cell &&
14509 child->row >= row && child->row < row + nrows &&
14510 gtk_widget_get_realized(child->widget))
14511 {
14512 gtk_container_remove(GTK_CONTAINER(sheet), child->widget);
14513 children = sheet->children;
14514 }
14515 else
14516 children = children->next;
14517 }
14518
14519 children = sheet->children;
14520 while (children)
14521 {
14522 child = (GtkSheetChild *)children->data;
14523
14524 if (child->attached_to_cell && child->row > row)
14525 child->row -= nrows;
14526 children = children->next;
14527 }
14528
14529 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
14530 return;
14531
14532 sheet->active_cell.row = -1;
14533 sheet->active_cell.col = -1;
14534
14535 /* if(sheet->state == GTK_SHEET_ROW_SELECTED)
14536 */
14537
14538 act_row = MIN(act_row, sheet->maxrow);
14539 act_row = MAX(act_row, 0);
14540
14541 _gtk_sheet_scrollbar_adjust(sheet);
14542 _gtk_sheet_redraw_internal(sheet, FALSE, TRUE);
14543
14544 gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col);
14545 }
14546
14547 /**
14548 * gtk_sheet_delete_columns:
14549 * @sheet: a #GtkSheet.
14550 * @col: column number.
14551 * @ncols: number of columns to be deleted.
14552 *
14553 * Delete @ncols columns starting from @col.
14554 */
14555 void
14556 gtk_sheet_delete_columns(GtkSheet *sheet, guint col, guint ncols)
14557 {
14558 GList *children;
14559 GtkSheetChild *child;
14560 gint act_row, act_col;
14561
14562 g_return_if_fail(sheet != NULL);
14563 g_return_if_fail(GTK_IS_SHEET(sheet));
14564
14565 ncols = MIN(ncols, sheet->maxcol - col + 1);
14566
14567 _gtk_sheet_hide_active_cell(sheet);
14568 gtk_sheet_real_unselect_range(sheet, NULL);
14569
14570 DeleteColumn(sheet, col, ncols);
14571
14572 children = sheet->children;
14573 while (children)
14574 {
14575 child = (GtkSheetChild *)children->data;
14576
14577 if (child->attached_to_cell &&
14578 child->col >= col && child->col < col + ncols &&
14579 gtk_widget_get_realized(child->widget))
14580 {
14581 gtk_container_remove(GTK_CONTAINER(sheet), child->widget);
14582 children = sheet->children;
14583 }
14584 else
14585 children = children->next;
14586 }
14587
14588 children = sheet->children;
14589 while (children)
14590 {
14591 child = (GtkSheetChild *)children->data;
14592
14593 if (child->attached_to_cell && child->col > col)
14594 child->col -= ncols;
14595 children = children->next;
14596 }
14597
14598 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
14599 return;
14600
14601 act_row = sheet->active_cell.row;
14602 act_col = sheet->active_cell.col;
14603
14604 sheet->active_cell.row = -1;
14605 sheet->active_cell.col = -1;
14606
14607 /* if(sheet->state == GTK_SHEET_COLUMN_SELECTED)
14608 */
14609
14610 act_col = MIN(act_col, sheet->maxcol);
14611 act_col = MAX(act_col, 0);
14612
14613 _gtk_sheet_scrollbar_adjust(sheet);
14614 _gtk_sheet_redraw_internal(sheet, TRUE, FALSE);
14615
14616 gtk_sheet_activate_cell(sheet, sheet->active_cell.row, sheet->active_cell.col);
14617 }
14618
14619 /**
14620 * gtk_sheet_range_set_background:
14621 * @sheet: a #GtkSheet.
14622 * @urange: a #GtkSheetRange.
14623 * @color: a #GdkColor.
14624 *
14625 * Set background color of the given range.
14626 */
14627 void
14628 gtk_sheet_range_set_background(GtkSheet *sheet,
14629 const GtkSheetRange *urange,
14630 const GdkColor *color)
14631 {
14632 gint i, j;
14633 GtkSheetRange range;
14634
14635 g_return_if_fail(sheet != NULL);
14636 g_return_if_fail(GTK_IS_SHEET(sheet));
14637
14638 if (!urange)
14639 range = sheet->range;
14640 else
14641 range = *urange;
14642
14643 #if GTK_SHEET_DEBUG_COLORS > 0
14644 g_debug("gtk_sheet_range_set_background: %s row %d-%d col %d-%d)",
14645 gdk_color_to_string(color), range.row0, range.rowi, range.col0, range.coli);
14646 #endif
14647
14648 for (i = range.row0; i <= range.rowi; i++) for (j = range.col0; j <= range.coli; j++)
14649 {
14650 GtkSheetCellAttr attributes;
14651 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14652
14653 if (color != NULL)
14654 attributes.background = *color;
14655 else
14656 attributes.background = sheet->bg_color;
14657
14658 gdk_colormap_alloc_color(gdk_colormap_get_system(), &attributes.background, FALSE, TRUE);
14659
14660 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14661 }
14662
14663 if (!GTK_SHEET_IS_FROZEN(sheet))
14664 _gtk_sheet_range_draw(sheet, &range, TRUE);
14665 }
14666
14667 /**
14668 * gtk_sheet_range_set_foreground:
14669 * @sheet: a #GtkSheet.
14670 * @urange: a #GtkSheetRange.
14671 * @color: a #GdkColor.
14672 *
14673 * Set foreground color of the given range.
14674 */
14675 void
14676 gtk_sheet_range_set_foreground(GtkSheet *sheet,
14677 const GtkSheetRange *urange,
14678 const GdkColor *color)
14679 {
14680 gint i, j;
14681 GtkSheetRange range;
14682
14683 g_return_if_fail(sheet != NULL);
14684 g_return_if_fail(GTK_IS_SHEET(sheet));
14685
14686 if (!urange)
14687 range = sheet->range;
14688 else
14689 range = *urange;
14690
14691 #if GTK_SHEET_DEBUG_COLORS > 0
14692 g_debug("gtk_sheet_range_set_foreground: %s row %d-%d col %d-%d)",
14693 gdk_color_to_string(color), range.row0, range.rowi, range.col0, range.coli);
14694 #endif
14695
14696 for (i = range.row0; i <= range.rowi; i++) for (j = range.col0; j <= range.coli; j++)
14697 {
14698 GtkSheetCellAttr attributes;
14699 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14700
14701 if (color != NULL)
14702 attributes.foreground = *color;
14703 else
14704 gdk_color_black(gdk_colormap_get_system(), &attributes.foreground);
14705
14706 gdk_colormap_alloc_color(gdk_colormap_get_system(), &attributes.foreground, FALSE, TRUE);
14707
14708 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14709 }
14710
14711 if (!GTK_SHEET_IS_FROZEN(sheet))
14712 _gtk_sheet_range_draw(sheet, &range, TRUE);
14713 }
14714
14715 /**
14716 * gtk_sheet_range_set_justification:
14717 * @sheet: a #GtkSheet.
14718 * @urange: a #GtkSheetRange.
14719 * @just: a #GtkJustification : GTK_JUSTIFY_LEFT, RIGHT, CENTER.
14720 *
14721 * Set text justification (GTK_JUSTIFY_LEFT, RIGHT, CENTER) of the given range.
14722 * The default value is GTK_JUSTIFY_LEFT. If autoformat is on, the default justification for numbers is GTK_JUSTIFY_RIGHT.
14723 */
14724 void
14725 gtk_sheet_range_set_justification(GtkSheet *sheet, const GtkSheetRange *urange,
14726 GtkJustification just)
14727 {
14728 gint i, j;
14729 GtkSheetRange range;
14730
14731 g_return_if_fail(sheet != NULL);
14732 g_return_if_fail(GTK_IS_SHEET(sheet));
14733
14734 if (!urange)
14735 range = sheet->range;
14736 else
14737 range = *urange;
14738
14739 for (i = range.row0; i <= range.rowi; i++)
14740 {
14741 for (j = range.col0; j <= range.coli; j++)
14742 {
14743 GtkSheetCellAttr attributes;
14744 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14745 attributes.justification = just;
14746 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14747 }
14748 }
14749
14750 range.col0 = sheet->view.col0;
14751 range.coli = sheet->view.coli;
14752
14753 if (!GTK_SHEET_IS_FROZEN(sheet))
14754 _gtk_sheet_range_draw(sheet, &range, TRUE);
14755 }
14756
14757
14758 /**
14759 * gtk_sheet_range_set_editable:
14760 * @sheet: a #GtkSheet.
14761 * @urange: a #GtkSheetRange
14762 * @editable: TRUE or FALSE
14763 *
14764 * Set if cell contents can be edited or not in the given range.
14765 */
14766 void
14767 gtk_sheet_range_set_editable(GtkSheet *sheet, const GtkSheetRange *urange, gboolean editable)
14768 {
14769 gint i, j;
14770 GtkSheetRange range;
14771
14772 g_return_if_fail(sheet != NULL);
14773 g_return_if_fail(GTK_IS_SHEET(sheet));
14774
14775 if (!urange)
14776 range = sheet->range;
14777 else
14778 range = *urange;
14779
14780 for (i = range.row0; i <= range.rowi; i++)
14781 {
14782 for (j = range.col0; j <= range.coli; j++)
14783 {
14784 GtkSheetCellAttr attributes;
14785 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14786 attributes.is_editable = editable;
14787 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14788 }
14789 }
14790
14791 if (!GTK_SHEET_IS_FROZEN(sheet))
14792 _gtk_sheet_range_draw(sheet, &range, TRUE);
14793 }
14794
14795 /**
14796 * gtk_sheet_range_set_visible:
14797 * @sheet: a #GtkSheet.
14798 * @urange: a #GtkSheetRange.
14799 * @visible: TRUE or FALSE.
14800 *
14801 * Set if cell contents are visible or not in the given range: accepted values are TRUE or FALSE.
14802 */
14803 void
14804 gtk_sheet_range_set_visible(GtkSheet *sheet, const GtkSheetRange *urange, gboolean visible)
14805 {
14806 gint i, j;
14807 GtkSheetRange range;
14808
14809 g_return_if_fail(sheet != NULL);
14810 g_return_if_fail(GTK_IS_SHEET(sheet));
14811
14812 if (!urange)
14813 range = sheet->range;
14814 else
14815 range = *urange;
14816
14817 for (i = range.row0; i <= range.rowi; i++)
14818 {
14819 for (j = range.col0; j <= range.coli; j++)
14820 {
14821 GtkSheetCellAttr attributes;
14822 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14823 attributes.is_visible = visible;
14824 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14825 }
14826 }
14827
14828 if (!GTK_SHEET_IS_FROZEN(sheet))
14829 _gtk_sheet_range_draw(sheet, &range, TRUE);
14830 }
14831
14832 /**
14833 * gtk_sheet_range_set_border:
14834 * @sheet: a #GtkSheet.
14835 * @urange: a #GtkSheetRange where we set border style.
14836 * @mask: CELL_LEFT_BORDER, CELL_RIGHT_BORDER, CELL_TOP_BORDER,CELL_BOTTOM_BORDER
14837 * @width: width of the border line in pixels
14838 * @line_style: GdkLineStyle for the border line
14839 *
14840 * Set cell border style in the given range.
14841 */
14842 void
14843 gtk_sheet_range_set_border(GtkSheet *sheet, const GtkSheetRange *urange, gint mask,
14844 guint width, gint line_style)
14845 {
14846 gint i, j;
14847 GtkSheetRange range;
14848
14849 g_return_if_fail(sheet != NULL);
14850 g_return_if_fail(GTK_IS_SHEET(sheet));
14851
14852 if (!urange)
14853 range = sheet->range;
14854 else
14855 range = *urange;
14856
14857 for (i = range.row0; i <= range.rowi; i++)
14858 {
14859 for (j = range.col0; j <= range.coli; j++)
14860 {
14861 GtkSheetCellAttr attributes;
14862 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14863 attributes.border.mask = mask;
14864 attributes.border.width = width;
14865 attributes.border.line_style = line_style;
14866 attributes.border.cap_style = GDK_CAP_NOT_LAST;
14867 attributes.border.join_style = GDK_JOIN_MITER;
14868 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14869 }
14870 }
14871
14872 range.row0--;
14873 range.col0--;
14874 range.rowi++;
14875 range.coli++;
14876
14877 if (!GTK_SHEET_IS_FROZEN(sheet))
14878 _gtk_sheet_range_draw(sheet, &range, TRUE);
14879 }
14880
14881 /**
14882 * gtk_sheet_range_set_border_color:
14883 * @sheet: a #GtkSheet.
14884 * @urange: a #GtkSheetRange where we set border color.
14885 * @color: a #GdkColor.
14886 *
14887 * Set border color for the given range.
14888 */
14889 void
14890 gtk_sheet_range_set_border_color(GtkSheet *sheet,
14891 const GtkSheetRange *urange,
14892 const GdkColor *color)
14893 {
14894 gint i, j;
14895 GtkSheetRange range;
14896
14897 g_return_if_fail(sheet != NULL);
14898 g_return_if_fail(GTK_IS_SHEET(sheet));
14899
14900 if (!urange)
14901 range = sheet->range;
14902 else
14903 range = *urange;
14904
14905 for (i = range.row0; i <= range.rowi; i++)
14906 {
14907 for (j = range.col0; j <= range.coli; j++)
14908 {
14909 GtkSheetCellAttr attributes;
14910 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14911 attributes.border.color = *color;
14912 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14913 }
14914 }
14915
14916 if (!GTK_SHEET_IS_FROZEN(sheet))
14917 _gtk_sheet_range_draw(sheet, &range, TRUE);
14918 }
14919
14920 /**
14921 * gtk_sheet_range_set_font:
14922 * @sheet: a #GtkSheet.
14923 * @urange: a #GtkSheetRange where we set font_desc.
14924 * @font_desc: (transfer none) a #PangoFontDescription.
14925 *
14926 * Set font_desc for the given range.
14927 */
14928 void
14929 gtk_sheet_range_set_font(GtkSheet *sheet,
14930 const GtkSheetRange *urange,
14931 PangoFontDescription *font_desc)
14932 {
14933 gint i, j;
14934 gint font_height;
14935 GtkSheetRange range;
14936 PangoContext *context;
14937 PangoFontMetrics *metrics;
14938
14939 g_return_if_fail(sheet != NULL);
14940 g_return_if_fail(GTK_IS_SHEET(sheet));
14941
14942 if (!urange)
14943 range = sheet->range;
14944 else
14945 range = *urange;
14946
14947 gtk_sheet_freeze(sheet);
14948
14949 context = gtk_widget_get_pango_context(GTK_WIDGET(sheet));
14950 metrics = pango_context_get_metrics(context,
14951 font_desc,
14952 pango_context_get_language(context));
14953 font_height = pango_font_metrics_get_descent(metrics) +
14954 pango_font_metrics_get_ascent(metrics);
14955 font_height = PANGO_PIXELS(font_height) + 2 * CELLOFFSET;
14956
14957 for (i = range.row0; i <= range.rowi; i++)
14958 {
14959 for (j = range.col0; j <= range.coli; j++)
14960 {
14961 GtkSheetCellAttr attributes;
14962 gtk_sheet_get_attributes(sheet, i, j, &attributes);
14963 attributes.font_desc = pango_font_description_copy(font_desc); /* copy */
14964 attributes.do_font_desc_free = TRUE;
14965
14966 if (font_height > sheet->row[i].height)
14967 {
14968 sheet->row[i].height = font_height;
14969 _gtk_sheet_recalc_top_ypixels(sheet);
14970 }
14971
14972 gtk_sheet_set_cell_attributes(sheet, i, j, attributes);
14973 }
14974 }
14975
14976 gtk_sheet_thaw(sheet);
14977 pango_font_metrics_unref(metrics);
14978 }
14979
14980 static void
14981 gtk_sheet_set_cell_attributes(GtkSheet *sheet,
14982 gint row, gint col,
14983 GtkSheetCellAttr attributes)
14984 {
14985 GtkSheetCell *cell;
14986
14987 if (row < 0 || row > sheet->maxrow)
14988 return;
14989 if (col < 0 || col > sheet->maxcol)
14990 return;
14991
14992 CheckCellData(sheet, row, col);
14993
14994 cell = sheet->data[row][col];
14995
14996 if (!cell->attributes)
14997 cell->attributes = g_new(GtkSheetCellAttr, 1);
14998
14999 *(cell->attributes) = attributes;
15000 }
15001
15002 /**
15003 * gtk_sheet_get_attributes:
15004 * @sheet: a #GtkSheet.
15005 * @row: row number
15006 * @col: column number
15007 * @attributes: #GtkSheetCellAttr of the given range
15008 *
15009 * Gett cell attributes of the given cell.
15010 *
15011 * Returns: TRUE means that the cell is currently allocated.
15012 */
15013 gboolean
15014 gtk_sheet_get_attributes(GtkSheet *sheet,
15015 gint row, gint col,
15016 GtkSheetCellAttr *attributes)
15017 {
15018 GtkSheetCell *cell;
15019
15020 g_return_val_if_fail(sheet != NULL, FALSE);
15021 g_return_val_if_fail(GTK_IS_SHEET(sheet), FALSE);
15022
15023 if ((row < 0 || row > sheet->maxrow) || (col < 0 || col > sheet->maxcol))
15024 {
15025 init_attributes(sheet, col, attributes);
15026 return (FALSE);
15027 }
15028
15029 if (row > sheet->maxallocrow || col > sheet->maxalloccol ||
15030 !sheet->data[row] || !sheet->data[row][col])
15031 {
15032 init_attributes(sheet, col, attributes);
15033 return (FALSE);
15034 }
15035
15036 cell = sheet->data[row][col];
15037
15038 if (!cell->attributes)
15039 {
15040 init_attributes(sheet, col, attributes);
15041 return (FALSE);
15042 }
15043
15044 *attributes = *(cell->attributes);
15045
15046 if (COLPTR(sheet, col)->justification != GTK_SHEET_COLUMN_DEFAULT_JUSTIFICATION)
15047 {
15048 attributes->justification = COLPTR(sheet, col)->justification;
15049 }
15050
15051 return (TRUE);
15052 }
15053
15054 static void
15055 init_attributes(GtkSheet *sheet, gint col, GtkSheetCellAttr *attributes)
15056 {
15057 /* DEFAULT VALUES */
15058 attributes->foreground = gtk_widget_get_style(GTK_WIDGET(sheet))->black;
15059 attributes->background = sheet->bg_color;
15060
15061 if (!gtk_widget_get_realized(GTK_WIDGET(sheet)))
15062 {
15063 GdkColormap *colormap;
15064 colormap = gdk_colormap_get_system();
15065 gdk_color_black(colormap, &attributes->foreground);
15066 attributes->background = sheet->bg_color;
15067 }
15068
15069 if (col < 0 || col > sheet->maxcol)
15070 attributes->justification = GTK_SHEET_COLUMN_DEFAULT_JUSTIFICATION;
15071 else
15072 attributes->justification = COLPTR(sheet, col)->justification;
15073
15074 attributes->border.width = 0;
15075 attributes->border.line_style = GDK_LINE_SOLID;
15076 attributes->border.cap_style = GDK_CAP_NOT_LAST;
15077 attributes->border.join_style = GDK_JOIN_MITER;
15078 attributes->border.mask = 0;
15079 attributes->border.color = gtk_widget_get_style(GTK_WIDGET(sheet))->black;
15080 attributes->is_editable = TRUE;
15081 attributes->is_sensitive = TRUE;
15082 attributes->can_focus = TRUE;
15083 attributes->is_visible = TRUE;
15084 attributes->font = gtk_widget_get_style(GTK_WIDGET(sheet))->private_font;
15085
15086 attributes->font_desc =
15087 gtk_widget_get_style(GTK_WIDGET(sheet))->font_desc; /* no copy */
15088 attributes->do_font_desc_free = FALSE;
15089 }
15090
15091 /**********************************************************************
15092 * Memory allocation routines:
15093 * AddRow & AddColumn allocate memory for GtkSheetColumn & GtkSheetRow structs.
15094 * InsertRow
15095 * InsertColumn
15096 * DeleteRow
15097 * DeleteColumn
15098 * GrowSheet allocates memory for the sheet cells contents using an array of
15099 * pointers. Alternative to this could be a linked list or a hash table.
15100 * CheckBounds checks whether the given cell is currently allocated or not.
15101 * If not, it calls to GrowSheet.
15102 **********************************************************************/
15103
15104 static void
15105 AddColumns(GtkSheet *sheet, gint position, gint ncols)
15106 {
15107 gint c;
15108 GtkSheetColumn *newobj;
15109
15110 g_assert(ncols >= 0);
15111 g_assert(position >= 0 && position <= sheet->maxcol + 1);
15112
15113 if (ncols > 0)
15114 {
15115 sheet->column = (GtkSheetColumn **)g_realloc(sheet->column,
15116 (sheet->maxcol + 1 + ncols)* sizeof(GtkSheetColumn *));
15117
15118 for (c = sheet->maxcol; c >= position; c--) /* make space */
15119 {
15120 sheet->column[c + ncols] = sheet->column[c];
15121 sheet->column[c] = NULL;
15122 }
15123
15124 for (c = 0; c < ncols; c++)
15125 {
15126 gint newidx = position + c;
15127
15128 newobj = g_object_new(G_TYPE_SHEET_COLUMN, NULL);
15129
15130 #if GTK_SHEET_DEBUG_FINALIZE > 0
15131 g_object_weak_ref(G_OBJECT(newobj), weak_notify, "Sheet-Column");
15132 #endif
15133
15134 newobj->sheet = sheet;
15135 sheet->column[newidx] = newobj;
15136
15137 gtk_widget_set_parent(GTK_WIDGET(newobj), GTK_WIDGET(sheet));
15138
15139 #if 0
15140 g_debug("Setting Column %p Parent to GtkSheet %p - got %p",
15141 newobj, sheet, gtk_widget_get_parent(GTK_WIDGET(newobj)));
15142 #endif
15143
15144 g_object_ref_sink(newobj);
15145 }
15146
15147 sheet->maxcol += ncols;
15148
15149 _gtk_sheet_reset_text_column(sheet, sheet->maxcol - ncols);
15150 _gtk_sheet_recalc_left_xpixels(sheet);
15151 }
15152 }
15153
15154 static void
15155 InsertColumn(GtkSheet *sheet, gint position, gint ncols)
15156 {
15157 gint r, c;
15158
15159 g_assert(ncols >= 0);
15160 g_assert(position >= 0);
15161
15162 AddColumns(sheet, position, ncols);
15163
15164 _gtk_sheet_reset_text_column(sheet, sheet->maxcol - ncols);
15165 _gtk_sheet_recalc_left_xpixels(sheet);
15166
15167 if (position <= sheet->maxalloccol) /* adjust allocated cells */
15168 {
15169 GrowSheet(sheet, 0, ncols);
15170
15171 for (r = 0; r <= sheet->maxallocrow; r++)
15172 {
15173 for (c = sheet->maxalloccol; c >= position + ncols; c--)
15174 {
15175 gtk_sheet_real_cell_clear(sheet, r, c, TRUE);
15176
15177 sheet->data[r][c] = sheet->data[r][c - ncols];
15178 if (sheet->data[r][c])
15179 sheet->data[r][c]->col = c;
15180 sheet->data[r][c - ncols] = NULL;
15181 }
15182 }
15183 }
15184 }
15185
15186 static void
15187 DeleteColumn(GtkSheet *sheet, gint position, gint ncols)
15188 {
15189 gint c, r;
15190
15191 g_assert(ncols >= 0);
15192 g_assert(position >= 0);
15193
15194 ncols = MIN(ncols, sheet->maxcol - position + 1);
15195
15196 if (ncols <= 0 || position > sheet->maxcol)
15197 return;
15198
15199 #if GTK_SHEET_DEBUG_ALLOCATION > 0
15200 g_debug("DeleteColumn: pos %d ncols %d mxr %d mxc %d mxar %d mxac %d ",
15201 position, ncols,
15202 sheet->maxrow, sheet->maxcol, sheet->maxallocrow, sheet->maxalloccol);
15203 #endif
15204
15205 for (c = position; c < position + ncols; c++) /* dispose columns */
15206 {
15207 sheet->column[c]->sheet = NULL;
15208
15209 g_object_unref(sheet->column[c]);
15210 sheet->column[c] = NULL;
15211 }
15212
15213 for (c = position; c <= sheet->maxcol - ncols; c++) /* shift columns into position*/
15214 {
15215 sheet->column[c] = sheet->column[c + ncols];
15216 }
15217
15218 for (c = sheet->maxcol - ncols + 1; c <= sheet->maxcol; c++) /* clear tail */
15219 {
15220 sheet->column[c] = NULL;
15221 }
15222
15223 /* to be done: shrink pointer pool via realloc */
15224
15225 if (position <= sheet->maxalloccol)
15226 {
15227 for (c = position; c <= sheet->maxcol - ncols; c++) /* shift column data */
15228 {
15229 if (c > sheet->maxalloccol) break;
15230
15231 for (r = 0; r <= sheet->maxallocrow; r++)
15232 {
15233 gtk_sheet_real_cell_clear(sheet, r, c, TRUE);
15234
15235 if (c + ncols <= sheet->maxalloccol)
15236 {
15237 sheet->data[r][c] = sheet->data[r][c + ncols];
15238 sheet->data[r][c + ncols] = NULL;
15239 if (sheet->data[r][c])
15240 sheet->data[r][c]->col = c;
15241 }
15242 }
15243 }
15244
15245 for (c = sheet->maxcol - ncols + 1; c <= sheet->maxcol; c++) /* clear tail */
15246 {
15247 if (c > sheet->maxalloccol) break;
15248
15249 for (r = 0; r <= sheet->maxallocrow; r++)
15250 {
15251 gtk_sheet_real_cell_clear(sheet, r, c, TRUE);
15252 }
15253 }
15254
15255 sheet->maxalloccol -= MIN(ncols, sheet->maxalloccol - position + 1);
15256 sheet->maxalloccol = MIN(sheet->maxalloccol, sheet->maxcol);
15257 }
15258
15259 sheet->maxcol -= ncols;
15260
15261 _gtk_sheet_range_fixup(sheet, &sheet->view);
15262 _gtk_sheet_range_fixup(sheet, &sheet->range);
15263
15264 _gtk_sheet_reset_text_column(sheet, position);
15265 _gtk_sheet_recalc_left_xpixels(sheet);
15266 }
15267
15268 static void
15269 AddRows(GtkSheet *sheet, gint position, gint nrows)
15270 {
15271 gint r;
15272
15273 g_assert(nrows >= 0);
15274 g_assert(position >= 0 && position <= sheet->maxrow + 1);
15275
15276 if (nrows > 0)
15277 {
15278 sheet->row = (GtkSheetRow *)g_realloc(sheet->row,
15279 (sheet->maxrow + 1 + nrows) * sizeof(GtkSheetRow));
15280
15281 for (r = sheet->maxrow; r >= position; r--) /* make space */
15282 {
15283 sheet->row[r + nrows] = sheet->row[r];
15284 _gtk_sheet_row_init(&sheet->row[r]);
15285 }
15286
15287 for (r = 0; r < nrows; r++)
15288 {
15289 gint newidx = position + r;
15290
15291 _gtk_sheet_row_init(&sheet->row[newidx]);
15292
15293 sheet->row[newidx].requisition = sheet->row[newidx].height =
15294 _gtk_sheet_row_default_height(GTK_WIDGET(sheet));
15295 }
15296 sheet->maxrow += nrows;
15297
15298 _gtk_sheet_recalc_top_ypixels(sheet);
15299 }
15300 }
15301
15302 static void
15303 InsertRow(GtkSheet *sheet, gint row, gint nrows)
15304 {
15305 gint r, c;
15306
15307 AddRows(sheet, row, nrows);
15308
15309 _gtk_sheet_recalc_top_ypixels(sheet);
15310
15311 if (row <= sheet->maxallocrow) /* adjust allocated cells */
15312 {
15313 GrowSheet(sheet, nrows, 0); /* append rows at end */
15314
15315 for (r = sheet->maxallocrow; r >= row + nrows; r--) /* swap new rows into position */
15316 {
15317 GtkSheetCell **auxdata = sheet->data[r];
15318
15319 sheet->data[r] = sheet->data[r - nrows];
15320 sheet->data[r - nrows] = auxdata;
15321
15322 /* new cells have no data yet to update */
15323
15324 GtkSheetCell **pp = sheet->data[r]; /* update row in existing cells */
15325 for (c = 0; c <= sheet->maxalloccol; c++, pp++)
15326 {
15327 if (*pp)
15328 (*pp)->row = r;
15329 }
15330 }
15331 }
15332 }
15333
15334 static void
15335 DeleteRow(GtkSheet *sheet, gint position, gint nrows)
15336 {
15337 gint r, c;
15338
15339 g_assert(nrows >= 0);
15340 g_assert(position >= 0);
15341
15342 nrows = MIN(nrows, sheet->maxrow - position + 1);
15343
15344 if (nrows <= 0 || position > sheet->maxrow)
15345 return;
15346
15347 #if GTK_SHEET_DEBUG_ALLOCATION > 0
15348 g_debug("DeleteRow: pos %d nrows %d mxr %d mxc %d mxar %d mxac %d ",
15349 position, nrows,
15350 sheet->maxrow, sheet->maxcol, sheet->maxallocrow, sheet->maxalloccol);
15351 #endif
15352
15353 for (r = position; r < position + nrows; r++) /* dispose row data */
15354 {
15355 gtk_sheet_row_finalize(&sheet->row[r]);
15356 }
15357
15358 for (r = position; r <= sheet->maxrow - nrows; r++) /* shift rows into position*/
15359 {
15360 sheet->row[r] = sheet->row[r + nrows];
15361 }
15362
15363 for (r = sheet->maxrow - nrows + 1; r <= sheet->maxrow; r++) /* clear tail */
15364 {
15365 _gtk_sheet_row_init(&sheet->row[r]);
15366 }
15367
15368 /* to be done: shrink pointer pool via realloc */
15369
15370 if (position <= sheet->maxallocrow)
15371 {
15372 for (r = position; r <= sheet->maxrow - nrows; r++) /* shift row data */
15373 {
15374 if (r > sheet->maxallocrow) break;
15375
15376 for (c = 0; c <= sheet->maxalloccol; c++) /* dispose cell data */
15377 {
15378 gtk_sheet_real_cell_clear(sheet, r, c, TRUE);
15379 }
15380
15381 if (sheet->data[r]) /* dispose row data pointer array */
15382 {
15383 g_free(sheet->data[r]);
15384 sheet->data[r] = NULL;
15385 }
15386
15387 if (r + nrows <= sheet->maxallocrow) /* shift tail down */
15388 {
15389 sheet->data[r] = sheet->data[r + nrows];
15390 sheet->data[r + nrows] = NULL;
15391
15392 GtkSheetCell **pp = sheet->data[r]; /* update row in existing cells */
15393 for (c = 0; c <= sheet->maxalloccol; c++, pp++)
15394 {
15395 if (*pp)
15396 (*pp)->row = r;
15397 }
15398 }
15399 }
15400
15401 for (r = sheet->maxrow - nrows + 1; r <= sheet->maxrow; r++) /* clear tail */
15402 {
15403 if (r > sheet->maxallocrow) break;
15404
15405 for (c = 0; c <= sheet->maxalloccol; c++) /* dispose cell data */
15406 {
15407 gtk_sheet_real_cell_clear(sheet, r, c, TRUE);
15408 }
15409
15410 if (sheet->data[r]) /* dispose row data pointer array */
15411 {
15412 g_free(sheet->data[r]);
15413 sheet->data[r] = NULL;
15414 }
15415 }
15416
15417 sheet->maxallocrow -= MIN(nrows, sheet->maxallocrow - position + 1);
15418 sheet->maxallocrow = MIN(sheet->maxallocrow, sheet->maxrow);
15419 }
15420
15421 sheet->maxrow -= nrows;
15422
15423 _gtk_sheet_range_fixup(sheet, &sheet->view);
15424 _gtk_sheet_range_fixup(sheet, &sheet->range);
15425
15426 _gtk_sheet_recalc_top_ypixels(sheet);
15427 }
15428
15429 static gint
15430 GrowSheet(GtkSheet *tbl, gint newrows, gint newcols)
15431 {
15432 gint r, c;
15433 gint inirow, inicol;
15434
15435 inirow = tbl->maxallocrow + 1;
15436 inicol = tbl->maxalloccol + 1;
15437
15438 tbl->maxalloccol = tbl->maxalloccol + newcols;
15439 tbl->maxallocrow = tbl->maxallocrow + newrows;
15440
15441 if (newrows > 0)
15442 {
15443 tbl->data = (GtkSheetCell ***)
15444 g_realloc(tbl->data, (tbl->maxallocrow + 1)*sizeof(GtkSheetCell **)+sizeof(double));
15445
15446 for (r = inirow; r <= tbl->maxallocrow; r++)
15447 {
15448 tbl->data[r] = (GtkSheetCell **)
15449 g_malloc((tbl->maxcol + 1)*sizeof(GtkSheetCell *)+sizeof(double));
15450
15451 for (c = 0; c < inicol; c++)
15452 {
15453 tbl->data[r][c] = NULL;
15454 }
15455 }
15456
15457 }
15458
15459 if (newcols > 0)
15460 {
15461 for (r = 0; r <= tbl->maxallocrow; r++)
15462 {
15463 tbl->data[r] = (GtkSheetCell **)
15464 g_realloc(tbl->data[r], (tbl->maxalloccol + 1)*sizeof(GtkSheetCell *)+sizeof(double));
15465 for (c = inicol; c <= tbl->maxalloccol; c++)
15466 {
15467 tbl->data[r][c] = NULL;
15468 }
15469 }
15470 }
15471
15472 return (0);
15473 }
15474
15475 static void
15476 CheckBounds(GtkSheet *sheet, gint row, gint col)
15477 {
15478 gint newrows = 0, newcols = 0;
15479
15480 if (col > sheet->maxalloccol)
15481 newcols = col - sheet->maxalloccol;
15482 if (row > sheet->maxallocrow)
15483 newrows = row - sheet->maxallocrow;
15484
15485 if (newrows > 0 || newcols > 0)
15486 GrowSheet(sheet, newrows, newcols);
15487 }
15488
15489
15490 /*
15491 * CheckCellData - verify existance of cell data, allocate if necessary
15492 *
15493 * @param sheet
15494 * @param row
15495 * @param col
15496 */
15497 static void
15498 CheckCellData(GtkSheet *sheet, const gint row, const gint col)
15499 {
15500 GtkSheetCell **cell;
15501
15502 g_return_if_fail(sheet != NULL);
15503 g_return_if_fail(GTK_IS_SHEET(sheet));
15504
15505 if (col > sheet->maxcol || row > sheet->maxrow)
15506 return;
15507 if (col < 0 || row < 0)
15508 return;
15509
15510 CheckBounds(sheet, row, col);
15511
15512 cell = &sheet->data[row][col];
15513
15514 if (!(*cell))
15515 (*cell) = gtk_sheet_cell_new();
15516
15517 (*cell)->row = row;
15518 (*cell)->col = col;
15519 }
15520
15521 /********************************************************************
15522 * Container Functions:
15523 * gtk_sheet_add
15524 * gtk_sheet_put
15525 * gtk_sheet_attach
15526 * gtk_sheet_remove
15527 * gtk_sheet_move_child
15528 * gtk_sheet_position_child
15529 * gtk_sheet_position_children
15530 * gtk_sheet_realize_child
15531 * gtk_sheet_get_child_at
15532 ********************************************************************/
15533
15534 /**
15535 * gtk_sheet_put:
15536 * @sheet: a #GtkSheet.
15537 * @child: GtkWidget to be put
15538 * @x: x coordinate where we put the widget
15539 * @y: y coordinate where we put the widget
15540 *
15541 * Add widgets to the sheet.
15542 * The widget is floating in one given position (x,y) regardless of the configurations of rows/columns.
15543 * This means that cells do not resize depending on the widgets' size.
15544 * You can resize it yourself or use gtk_sheet_attach_*()
15545 * You may remove it with gtk_container_remove(GTK_CONTAINER(sheet), GtkWidget *child);
15546 *
15547 * Returns: (transfer none) TRUE means that the cell is currently allocated.
15548 */
15549 GtkSheetChild *
15550 gtk_sheet_put(GtkSheet *sheet, GtkWidget *child, gint x, gint y)
15551 {
15552 GtkRequisition child_requisition;
15553 GtkSheetChild *child_info;
15554
15555 g_return_val_if_fail(sheet != NULL, NULL);
15556 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
15557 g_return_val_if_fail(child != NULL, NULL);
15558 g_return_val_if_fail(gtk_widget_get_parent(child) == NULL, NULL);
15559
15560 #if GTK_SHEET_DEBUG_CHILDREN > 0
15561 g_debug("gtk_sheet_put: %p %s child %p",
15562 sheet, gtk_widget_get_name(sheet), child);
15563 #endif
15564
15565 child_info = g_new(GtkSheetChild, 1);
15566 child_info->widget = child;
15567 child_info->x = x;
15568 child_info->y = y;
15569 child_info->attached_to_cell = FALSE;
15570 child_info->floating = TRUE;
15571 child_info->xpadding = child_info->ypadding = 0;
15572 child_info->xexpand = child_info->yexpand = FALSE;
15573 child_info->xshrink = child_info->yshrink = FALSE;
15574 child_info->xfill = child_info->yfill = FALSE;
15575
15576 sheet->children = g_list_append(sheet->children, child_info);
15577 g_object_ref(child);
15578 gtk_widget_set_parent(child, GTK_WIDGET(sheet));
15579
15580 gtk_widget_size_request(child, &child_requisition);
15581
15582 if (gtk_widget_get_visible(GTK_WIDGET(sheet)))
15583 {
15584 if (gtk_widget_get_realized(GTK_WIDGET(sheet)) &&
15585 (!gtk_widget_get_realized(child) || gtk_widget_get_has_window(child)))
15586 gtk_sheet_realize_child(sheet, child_info);
15587
15588 if (gtk_widget_get_mapped(GTK_WIDGET(sheet)) &&
15589 !gtk_widget_get_mapped(child))
15590 gtk_widget_map(child);
15591 }
15592
15593 gtk_sheet_position_child(sheet, child_info);
15594
15595 /* This will avoid drawing on the titles */
15596
15597 if (gtk_widget_get_realized(GTK_WIDGET(sheet)))
15598 {
15599 if (sheet->row_titles_visible)
15600 gdk_window_show(sheet->row_title_window);
15601 if (sheet->column_titles_visible)
15602 gdk_window_show(sheet->column_title_window);
15603 }
15604
15605 return (child_info);
15606 }
15607
15608 /**
15609 * gtk_sheet_attach_floating:
15610 * @sheet: a #GtkSheet.
15611 * @widget: #GtkWidget to be put
15612 * @row: row number
15613 * @col: column number
15614 *
15615 * The widget is attached to the top-left corner of a cell (row,column) and moves with it when you change width,
15616 * height, or you delete of add row/columns
15617 */
15618 void
15619 gtk_sheet_attach_floating(GtkSheet *sheet,
15620 GtkWidget *widget,
15621 gint row, gint col)
15622 {
15623 GdkRectangle area;
15624 GtkSheetChild *child;
15625
15626 if (row < 0 || col < 0)
15627 {
15628 gtk_sheet_button_attach(sheet, widget, row, col);
15629 return;
15630 }
15631
15632 gtk_sheet_get_cell_area(sheet, row, col, &area);
15633 child = gtk_sheet_put(sheet, widget, area.x, area.y);
15634 child->attached_to_cell = TRUE;
15635 child->row = row;
15636 child->col = col;
15637 }
15638
15639 /**
15640 * gtk_sheet_attach_default:
15641 * @sheet: a #GtkSheet.
15642 * @widget: #GtkWidget to be put
15643 * @row: row number
15644 * @col: column number
15645 *
15646 * Attaches a child widget to the given cell with the 0,0 alignments.
15647 * Works basically like gtk_table_attach, with the same options, the widget is confined in the cell, and whether it fills the
15648 * cell, expands with it, or shrinks with it, depending on the options.
15649 * The child is reallocated each time the column or row changes, keeping attached to the same cell.
15650 * It's in fact gtk_sheet_attach() with GTK_EXPAND set.
15651 */
15652 void
15653 gtk_sheet_attach_default(GtkSheet *sheet,
15654 GtkWidget *widget,
15655 gint row, gint col)
15656 {
15657 if (row < 0 || col < 0)
15658 {
15659 gtk_sheet_button_attach(sheet, widget, row, col);
15660 return;
15661 }
15662
15663 gtk_sheet_attach(sheet, widget, row, col, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
15664 }
15665
15666 /**
15667 * gtk_sheet_attach:
15668 * @sheet: a #GtkSheet.
15669 * @widget: #GtkWidget to be put
15670 * @row: row number
15671 * @col: column number
15672 * @xoptions: if set GTK_EXPAND cell will expand/shrink on x direction
15673 * @yoptions: if set GTK_EXPAND cell will expand/shrink on y direction
15674 * @xpadding: x coordinate of the alignment
15675 * @ypadding: y coordinate of the alignment
15676 *
15677 * Attaches a child widget to the given cell with the given alignments.
15678 * Works basically like gtk_table_attach, with the same options, the widget is confined in the cell, and whether it fills the
15679 * cell, expands with it, or shrinks with it, depending on the options , if GTK_EXPAND is set.
15680 * The child is reallocated each time the column or row changes, keeping attached to the same cell.
15681 */
15682 void
15683 gtk_sheet_attach(GtkSheet *sheet,
15684 GtkWidget *widget,
15685 gint row, gint col,
15686 gint xoptions,
15687 gint yoptions,
15688 gint xpadding,
15689 gint ypadding)
15690 {
15691 GdkRectangle area;
15692 GtkSheetChild *child = NULL;
15693
15694 g_return_if_fail(sheet != NULL);
15695 g_return_if_fail(GTK_IS_SHEET(sheet));
15696 g_return_if_fail(widget != NULL);
15697
15698 if (row < 0 || col < 0)
15699 {
15700 gtk_sheet_button_attach(sheet, widget, row, col);
15701 return;
15702 }
15703
15704 #if GTK_SHEET_DEBUG_CHILDREN > 0
15705 g_debug("gtk_sheet_attach: %p %s widget %p",
15706 sheet, gtk_widget_get_name(sheet), widget);
15707 #endif
15708
15709 child = g_new0(GtkSheetChild, 1);
15710 child->attached_to_cell = TRUE;
15711 child->floating = FALSE;
15712 child->widget = widget;
15713 child->row = row;
15714 child->col = col;
15715 child->xpadding = xpadding;
15716 child->ypadding = ypadding;
15717 child->xexpand = (xoptions & GTK_EXPAND) != 0;
15718 child->yexpand = (yoptions & GTK_EXPAND) != 0;
15719 child->xshrink = (xoptions & GTK_SHRINK) != 0;
15720 child->yshrink = (yoptions & GTK_SHRINK) != 0;
15721 child->xfill = (xoptions & GTK_FILL) != 0;
15722 child->yfill = (yoptions & GTK_FILL) != 0;
15723
15724 sheet->children = g_list_append(sheet->children, child);
15725 g_object_ref(child->widget);
15726 gtk_sheet_get_cell_area(sheet, row, col, &area);
15727
15728 child->x = area.x + child->xpadding;
15729 child->y = area.y + child->ypadding;
15730
15731 if (gtk_widget_get_visible(GTK_WIDGET(sheet)))
15732 {
15733 if (gtk_widget_get_realized(GTK_WIDGET(sheet)) &&
15734 (!gtk_widget_get_realized(widget) || gtk_widget_get_has_window(widget)))
15735 gtk_sheet_realize_child(sheet, child);
15736
15737 if (gtk_widget_get_mapped(GTK_WIDGET(sheet)) &&
15738 !gtk_widget_get_mapped(widget))
15739 gtk_widget_map(widget);
15740 }
15741
15742 gtk_sheet_position_child(sheet, child);
15743
15744 /* This will avoid drawing on the titles */
15745
15746 if (gtk_widget_get_realized(GTK_WIDGET(sheet)))
15747 {
15748 if (GTK_SHEET_ROW_TITLES_VISIBLE(sheet))
15749 gdk_window_show(sheet->row_title_window);
15750 if (GTK_SHEET_COL_TITLES_VISIBLE(sheet))
15751 gdk_window_show(sheet->column_title_window);
15752 }
15753 }
15754
15755 /**
15756 * gtk_sheet_button_attach:
15757 * @sheet: a #GtkSheet.
15758 * @widget: #GtkWidget to be put
15759 * @row: row number
15760 * @col: column number
15761 *
15762 * Button attach works like cell attach but for the buttons.
15763 */
15764 void
15765 gtk_sheet_button_attach(GtkSheet *sheet,
15766 GtkWidget *widget,
15767 gint row, gint col)
15768 {
15769 GtkSheetButton *button;
15770 GtkSheetChild *child;
15771 GtkRequisition button_requisition;
15772
15773 if (row >= 0 && col >= 0)
15774 return;
15775 if (row < 0 && col < 0)
15776 return;
15777
15778 #if GTK_SHEET_DEBUG_CHILDREN > 0
15779 g_debug("gtk_sheet_button_attach: called");
15780 #endif
15781
15782 child = g_new(GtkSheetChild, 1);
15783 child->widget = widget;
15784 child->x = 0;
15785 child->y = 0;
15786 child->attached_to_cell = TRUE;
15787 child->floating = FALSE;
15788 child->row = row;
15789 child->col = col;
15790 child->xpadding = child->ypadding = 0;
15791 child->xshrink = child->yshrink = FALSE;
15792 child->xfill = child->yfill = FALSE;
15793
15794 if (row == -1)
15795 {
15796 button = &COLPTR(sheet, col)->button;
15797 button->child = child;
15798 }
15799 else
15800 {
15801 button = &sheet->row[row].button;
15802 button->child = child;
15803 }
15804
15805 sheet->children = g_list_append(sheet->children, child);
15806 g_object_ref(child);
15807
15808 _gtk_sheet_button_size_request(sheet, button, &button_requisition);
15809
15810 if (row == -1)
15811 {
15812 if (button_requisition.height > sheet->column_title_area.height)
15813 sheet->column_title_area.height = button_requisition.height;
15814 if (button_requisition.width > COLPTR(sheet, col)->width)
15815 COLPTR(sheet, col)->width = button_requisition.width;
15816 }
15817
15818 if (col == -1)
15819 {
15820 if (button_requisition.width > sheet->row_title_area.width)
15821 sheet->row_title_area.width = button_requisition.width;
15822 if (button_requisition.height > sheet->row[row].height)
15823 sheet->row[row].height = button_requisition.height;
15824 }
15825
15826 if (gtk_widget_get_visible(GTK_WIDGET(sheet)))
15827 {
15828 if (gtk_widget_get_realized(GTK_WIDGET(sheet)) &&
15829 (!gtk_widget_get_realized(widget) || gtk_widget_get_has_window(widget)))
15830 gtk_sheet_realize_child(sheet, child);
15831
15832 if (gtk_widget_get_mapped(GTK_WIDGET(sheet)) &&
15833 !gtk_widget_get_mapped(widget))
15834 gtk_widget_map(widget);
15835 }
15836
15837 if (row == -1)
15838 _gtk_sheet_column_buttons_size_allocate(sheet);
15839 if (col == -1)
15840 size_allocate_row_title_buttons(sheet);
15841 }
15842
15843 static void
15844 label_size_request(GtkSheet *sheet, gchar *label, GtkRequisition *req)
15845 {
15846 gchar *words;
15847 gchar word[1000];
15848 gint n = 0;
15849 gint row_height = _gtk_sheet_row_default_height(GTK_WIDGET(sheet)) - 2 * CELLOFFSET + 2;
15850
15851 req->height = 0;
15852 req->width = 0;
15853 words = label;
15854
15855 while (words && *words != '\0')
15856 {
15857 if (*words == '\n' || *(words + 1) == '\0')
15858 {
15859 guint text_width, text_height;
15860
15861 req->height += row_height;
15862
15863 word[n] = '\0';
15864
15865 _get_string_extent(sheet, NULL,
15866 gtk_widget_get_style(GTK_WIDGET(sheet))->font_desc,
15867 word, &text_width, &text_height);
15868 req->width = MAX(req->width, text_width);
15869 n = 0;
15870 }
15871 else
15872 {
15873 word[n++] = *words;
15874 }
15875 words++;
15876 }
15877
15878 if (n > 0)
15879 req->height -= 2;
15880 }
15881
15882 /**
15883 * _gtk_sheet_button_size_request:
15884 * @sheet: the #GtkSheet
15885 * @button: the #GtkSheetButton requested
15886 * @button_requisition: the requisition
15887 *
15888 * size request handler for all sheet buttons
15889 */
15890 void
15891 _gtk_sheet_button_size_request(GtkSheet *sheet,
15892 GtkSheetButton *button,
15893 GtkRequisition *button_requisition)
15894 {
15895 GtkRequisition requisition;
15896 GtkRequisition label_requisition;
15897
15898 if (gtk_sheet_autoresize(sheet) && button->label && button->label[0])
15899 {
15900 label_size_request(sheet, button->label, &label_requisition);
15901 label_requisition.width += 2 * CELLOFFSET;
15902 label_requisition.height += 2 * CELLOFFSET;
15903 }
15904 else
15905 {
15906 label_requisition.height = _gtk_sheet_row_default_height(GTK_WIDGET(sheet));
15907 label_requisition.width = GTK_SHEET_COLUMN_MIN_WIDTH;
15908 }
15909
15910 if (button->child)
15911 {
15912 gtk_widget_size_request(button->child->widget, &requisition);
15913 requisition.width += 2 * button->child->xpadding;
15914 requisition.height += 2 * button->child->ypadding;
15915 requisition.width += 2 * gtk_widget_get_style(sheet->button)->xthickness;
15916 requisition.height += 2 * gtk_widget_get_style(sheet->button)->ythickness;
15917 }
15918 else
15919 {
15920 requisition.height = _gtk_sheet_row_default_height(GTK_WIDGET(sheet));
15921 requisition.width = GTK_SHEET_COLUMN_MIN_WIDTH;
15922 }
15923
15924 *button_requisition = requisition;
15925 button_requisition->width = MAX(requisition.width, label_requisition.width);
15926 button_requisition->height = MAX(requisition.height, label_requisition.height);
15927
15928 }
15929
15930 static void
15931 gtk_sheet_row_size_request(GtkSheet *sheet,
15932 gint row,
15933 guint *requisition)
15934 {
15935 GtkRequisition button_requisition;
15936 GList *children;
15937
15938 _gtk_sheet_button_size_request(sheet, &sheet->row[row].button, &button_requisition);
15939
15940 *requisition = button_requisition.height;
15941
15942 children = sheet->children;
15943 while (children)
15944 {
15945 GtkSheetChild *child = (GtkSheetChild *)children->data;
15946 GtkRequisition child_requisition;
15947
15948 if (child->attached_to_cell && child->row == row && child->col != -1 && !child->floating && !child->yshrink)
15949 {
15950 gtk_widget_get_child_requisition(child->widget, &child_requisition);
15951
15952 if (child_requisition.height + 2 * child->ypadding > *requisition)
15953 *requisition = child_requisition.height + 2 * child->ypadding;
15954 }
15955 children = children->next;
15956 }
15957
15958 sheet->row[row].requisition = *requisition;
15959 }
15960
15961 /**
15962 * gtk_sheet_move_child:
15963 * @sheet: a #GtkSheet.
15964 * @widget: #GtkWidget to be put.
15965 * @x: x coord at which we move the widget.
15966 * @y: y coord at which we move the widget.
15967 *
15968 * Move widgets added with gtk_sheet_put() in the sheet.
15969 */
15970 void
15971 gtk_sheet_move_child(GtkSheet *sheet, GtkWidget *widget, gint x, gint y)
15972 {
15973 GtkSheetChild *child;
15974 GList *children;
15975
15976 g_return_if_fail(sheet != NULL);
15977 g_return_if_fail(GTK_IS_SHEET(sheet));
15978
15979 children = sheet->children;
15980 while (children)
15981 {
15982 child = children->data;
15983
15984 if (child->widget == widget)
15985 {
15986 child->x = x;
15987 child->y = y;
15988 child->row = _gtk_sheet_row_from_ypixel(sheet, y);
15989 child->col = _gtk_sheet_column_from_xpixel(sheet, x);
15990 gtk_sheet_position_child(sheet, child);
15991 return;
15992 }
15993
15994 children = children->next;
15995 }
15996
15997 g_warning("Widget must be a GtkSheet child");
15998 }
15999
16000 static void
16001 gtk_sheet_position_child(GtkSheet *sheet, GtkSheetChild *child)
16002 {
16003 GtkRequisition child_requisition;
16004 GtkAllocation child_allocation;
16005 gint xoffset = 0;
16006 gint yoffset = 0;
16007 gint x = 0, y = 0;
16008 GdkRectangle area;
16009
16010 /* PR#99118 - we cannot reposition all children while scrolling because
16011 with many offside rows it will consume lots of CPU and incredibly
16012 slow down scrolling.
16013
16014 If we do not reposition all childs, we have to hide them whenn going
16015 off screen, in order not to stray around. We're using unmap/map here
16016 in order to leave hide/show available for the application
16017 */
16018
16019 if (child->attached_to_cell)
16020 {
16021 if ((child->row < MIN_VIEW_ROW(sheet) || child->row > MAX_VIEW_ROW(sheet))
16022 || (child->col < MIN_VIEW_COLUMN(sheet) || child->col > MAX_VIEW_COLUMN(sheet))
16023 )
16024 {
16025 gtk_widget_unmap(child->widget);
16026 return;
16027 }
16028 if (gtk_widget_get_realized(child->widget)
16029 && !gtk_widget_get_mapped(child->widget))
16030 {
16031 gtk_widget_map(child->widget);
16032 }
16033 }
16034
16035 gtk_widget_get_child_requisition(child->widget, &child_requisition);
16036
16037 if (sheet->column_titles_visible)
16038 yoffset = sheet->column_title_area.height;
16039
16040 if (sheet->row_titles_visible)
16041 xoffset = sheet->row_title_area.width;
16042
16043 if (child->attached_to_cell)
16044 {
16045 gtk_sheet_get_cell_area(sheet, child->row, child->col, &area);
16046
16047 child->x = area.x + child->xpadding;
16048 child->y = area.y + child->ypadding;
16049
16050 if (!child->floating)
16051 {
16052 if (child_requisition.width + 2 * child->xpadding <= COLPTR(sheet, child->col)->width)
16053 {
16054 if (child->xfill)
16055 {
16056 child_requisition.width = child_allocation.width = COLPTR(sheet, child->col)->width - 2 * child->xpadding;
16057 }
16058 else
16059 {
16060 if (child->xexpand)
16061 {
16062 child->x = area.x + COLPTR(sheet, child->col)->width / 2 -
16063 child_requisition.width / 2;
16064 }
16065 child_allocation.width = child_requisition.width;
16066 }
16067 }
16068 else
16069 {
16070 if (!child->xshrink)
16071 {
16072 #if GTK_SHEET_DEBUG_SIZE > 0
16073 g_debug("gtk_sheet_position_child[%d]: set width %d",
16074 child->col, child_requisition.width + 2 * child->xpadding);
16075 #endif
16076 gtk_sheet_set_column_width(sheet,
16077 child->col, child_requisition.width + 2 * child->xpadding);
16078 }
16079 child_allocation.width = COLPTR(sheet, child->col)->width - 2 * child->xpadding;
16080 }
16081
16082 if (child_requisition.height + 2 * child->ypadding <= sheet->row[child->row].height)
16083 {
16084 if (child->yfill)
16085 {
16086 child_requisition.height = child_allocation.height = sheet->row[child->row].height - 2 * child->ypadding;
16087 }
16088 else
16089 {
16090 if (child->yexpand)
16091 {
16092 child->y = area.y + sheet->row[child->row].height / 2 -
16093 child_requisition.height / 2;
16094 }
16095 child_allocation.height = child_requisition.height;
16096 }
16097 }
16098 else
16099 {
16100 if (!child->yshrink)
16101 {
16102 gtk_sheet_set_row_height(sheet, child->row, child_requisition.height + 2 * child->ypadding);
16103 }
16104 child_allocation.height = sheet->row[child->row].height - 2 * child->ypadding;
16105 }
16106 }
16107 else
16108 {
16109 child_allocation.width = child_requisition.width;
16110 child_allocation.height = child_requisition.height;
16111 }
16112
16113 x = child_allocation.x = child->x + xoffset;
16114 y = child_allocation.y = child->y + yoffset;
16115 }
16116 else /* not attached_to_cell */
16117 {
16118 x = child_allocation.x = child->x + sheet->hoffset + xoffset;
16119 x = child_allocation.x = child->x + xoffset;
16120 y = child_allocation.y = child->y + sheet->voffset + yoffset;
16121 y = child_allocation.y = child->y + yoffset;
16122 child_allocation.width = child_requisition.width;
16123 child_allocation.height = child_requisition.height;
16124 }
16125
16126 gtk_widget_size_allocate(child->widget, &child_allocation);
16127 gtk_widget_queue_draw(child->widget);
16128 }
16129
16130 /*
16131 * gtk_sheet_forall_handler:
16132 *
16133 * this is the #GtkSheet container child enumeration handler
16134 *
16135 * @param container the #GtkSheet
16136 * @param include_internals
16137 * Flag wether to include internal childs
16138 * @param callback a callback function
16139 * @param callback_data
16140 * callback user data
16141 */
16142 static void
16143 gtk_sheet_forall_handler(GtkContainer *container,
16144 gboolean include_internals,
16145 GtkCallback callback,
16146 gpointer callback_data)
16147 {
16148 GtkSheet *sheet;
16149 GtkSheetChild *child;
16150 GList *children;
16151
16152 g_return_if_fail(GTK_IS_SHEET(container));
16153 g_return_if_fail(callback != NULL);
16154
16155 sheet = GTK_SHEET(container);
16156 children = sheet->children;
16157
16158 #if GTK_SHEET_DEBUG_CHILDREN > 1
16159 g_debug("gtk_sheet_forall_handler: Sheet <%s>",
16160 gtk_widget_get_name(GTK_WIDGET(sheet)));
16161 #endif
16162
16163 while (children)
16164 {
16165 child = children->data;
16166 children = children->next;
16167
16168 #if GTK_SHEET_DEBUG_CHILDREN > 1
16169 g_debug("gtk_sheet_forall_handler: L1 %p", child->widget);
16170 #endif
16171
16172 if (G_IS_OBJECT(child->widget)
16173 && GTK_IS_WIDGET(child->widget)
16174 )
16175 {
16176 #if GTK_SHEET_DEBUG_CHILDREN > 1
16177 g_debug("gtk_sheet_forall_handler: L2 %p", child->widget);
16178 #endif
16179 (*callback)(child->widget, callback_data);
16180 }
16181 }
16182
16183 #if GTK_SHEET_DEBUG_CHILDREN > 1
16184 g_debug("gtk_sheet_forall_handler: B1 %p %d",
16185 sheet->button, GTK_IS_WIDGET(sheet->button));
16186 #endif
16187
16188 if (sheet->button
16189 && G_IS_OBJECT(sheet->button)
16190 && GTK_IS_WIDGET(sheet->button)
16191 )
16192 {
16193 #if GTK_SHEET_DEBUG_CHILDREN > 1
16194 g_debug("gtk_sheet_forall_handler: B2 %p", sheet->button);
16195 #endif
16196 g_object_ref(sheet->button);
16197 (*callback)(sheet->button, callback_data);
16198 g_object_unref(sheet->button);
16199 }
16200
16201 #if GTK_SHEET_DEBUG_CHILDREN > 1
16202 g_debug("gtk_sheet_forall_handler: C1 %p %d",
16203 sheet->sheet_entry, GTK_IS_WIDGET(sheet->sheet_entry));
16204 #endif
16205
16206 if (sheet->sheet_entry
16207 && G_IS_OBJECT(sheet->sheet_entry)
16208 && GTK_IS_WIDGET(sheet->sheet_entry)
16209 )
16210 {
16211 #if GTK_SHEET_DEBUG_CHILDREN > 1
16212 g_debug("gtk_sheet_forall_handler: C2 %p IsObject %d IsWidget %d",
16213 sheet->sheet_entry,
16214 G_IS_OBJECT(sheet->sheet_entry),
16215 GTK_IS_WIDGET(sheet->sheet_entry));
16216 #endif
16217 g_object_ref(sheet->sheet_entry);
16218 (*callback)(sheet->sheet_entry, callback_data);
16219 g_object_unref(sheet->sheet_entry);
16220 }
16221 }
16222
16223
16224 static void
16225 gtk_sheet_position_children(GtkSheet *sheet)
16226 {
16227 GList *children;
16228 GtkSheetChild *child;
16229
16230 children = sheet->children;
16231
16232 while (children)
16233 {
16234 child = (GtkSheetChild *)children->data;
16235
16236 if (child->col != -1 && child->row != -1)
16237 gtk_sheet_position_child(sheet, child);
16238
16239 if (child->row == -1)
16240 {
16241 if (child->col < MIN_VIEW_COLUMN(sheet) ||
16242 child->col > MAX_VIEW_COLUMN(sheet))
16243 _gtk_sheet_child_hide(child);
16244 else
16245 _gtk_sheet_child_show(child);
16246 }
16247 if (child->col == -1)
16248 {
16249 if (child->row < MIN_VIEW_ROW(sheet) ||
16250 child->row > MAX_VIEW_ROW(sheet))
16251 _gtk_sheet_child_hide(child);
16252 else
16253 _gtk_sheet_child_show(child);
16254 }
16255
16256 children = children->next;
16257 }
16258
16259 }
16260
16261 /*
16262 * gtk_sheet_remove_handler:
16263 *
16264 * this is the #GtkSheet container class "remove" handler
16265 *
16266 * @param container the #GtkSheet
16267 * @param widget the #GtkWidget to be removed
16268 */
16269 static void
16270 gtk_sheet_remove_handler(GtkContainer *container, GtkWidget *widget)
16271 {
16272 GtkSheet *sheet;
16273 GList *children;
16274 GtkSheetChild *child = NULL;
16275
16276 g_return_if_fail(container != NULL);
16277 g_return_if_fail(GTK_IS_SHEET(container));
16278
16279 sheet = GTK_SHEET(container);
16280
16281 children = sheet->children;
16282
16283 while (children)
16284 {
16285 child = (GtkSheetChild *)children->data;
16286
16287 if (child->widget == widget)
16288 break;
16289
16290 children = children->next;
16291 }
16292
16293 if (children)
16294 {
16295 if (child->row == -1)
16296 sheet->row[child->col].button.child = NULL;
16297
16298 if (child->col == -1)
16299 COLPTR(sheet, child->row)->button.child = NULL;
16300
16301 #if GTK_SHEET_DEBUG_CHILDREN > 0
16302 g_debug("gtk_sheet_remove_handler: %p %s widget %p",
16303 sheet, gtk_widget_get_name(sheet), widget);
16304 #endif
16305
16306 gtk_widget_unparent(widget);
16307 if (G_IS_OBJECT(child->widget))
16308 g_object_unref(child->widget);
16309 child->widget = NULL;
16310
16311 sheet->children = g_list_remove_link(sheet->children, children);
16312 g_list_free_1(children);
16313 g_free(child);
16314 }
16315
16316 }
16317
16318 static void
16319 gtk_sheet_realize_child(GtkSheet *sheet, GtkSheetChild *child)
16320 {
16321 GtkWidget *widget;
16322
16323 widget = GTK_WIDGET(sheet);
16324
16325 if (gtk_widget_get_realized(widget))
16326 {
16327 if (child->row == -1)
16328 gtk_widget_set_parent_window(child->widget, sheet->column_title_window);
16329 else if (child->col == -1)
16330 gtk_widget_set_parent_window(child->widget, sheet->row_title_window);
16331 else
16332 gtk_widget_set_parent_window(child->widget, sheet->sheet_window);
16333 }
16334
16335 gtk_widget_set_parent(child->widget, widget);
16336 }
16337
16338
16339 /**
16340 * gtk_sheet_get_child_at:
16341 * @sheet: a #GtkSheet.
16342 * @row: row number
16343 * @col: column number
16344 *
16345 * Get the child attached at @row,@col.
16346 *
16347 * returns: the #GtkSheetChild attached to @row,@col or NULL
16348 */
16349 const GtkSheetChild *
16350 gtk_sheet_get_child_at(GtkSheet *sheet, gint row, gint col)
16351 {
16352 GList *children;
16353
16354 g_return_val_if_fail(sheet != NULL, NULL);
16355 g_return_val_if_fail(GTK_IS_SHEET(sheet), NULL);
16356
16357 children = sheet->children;
16358
16359 while (children)
16360 {
16361 const GtkSheetChild *child = (GtkSheetChild *)children->data;
16362
16363 if (child->attached_to_cell)
16364 {
16365 if (child->row == row && child->col == col)
16366 return (child);
16367 }
16368
16369 children = children->next;
16370 }
16371 return (NULL);
16372 }
16373
16374 /**
16375 * _gtk_sheet_child_hide:
16376 * @child: the child
16377 *
16378 * gtk_widget_hide(child)
16379 */
16380 void
16381 _gtk_sheet_child_hide(GtkSheetChild *child)
16382 {
16383 g_return_if_fail(child != NULL);
16384 gtk_widget_hide(child->widget);
16385 }
16386
16387 /**
16388 * _gtk_sheet_child_show:
16389 * @child: the child
16390 *
16391 * gtk_widget_show(child)
16392 */
16393 void
16394 _gtk_sheet_child_show(GtkSheetChild *child)
16395 {
16396 g_return_if_fail(child != NULL);
16397 gtk_widget_show(child->widget);
16398 }
16399