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 		&gtk_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 		&gtk_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