1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * e-table-item.c
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 *
17 *
18 * Authors:
19 * Chris Lahey <clahey@ximian.com>
20 * Miguel de Icaza <miguel@gnu.org>
21 *
22 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 */
24 /*
25 * TODO:
26 * Add a border to the thing, so that focusing works properly.
27 */
28
29 #include "evolution-config.h"
30
31 #include "e-table-item.h"
32
33 #include <math.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37
38 #include <gtk/gtk.h>
39 #include <glib/gi18n.h>
40 #include <gdk/gdkkeysyms.h>
41
42 #include "e-canvas-utils.h"
43 #include "e-canvas.h"
44 #include "e-cell.h"
45 #include "e-cell-text.h"
46 #include "e-marshal.h"
47 #include "e-table-subset.h"
48 #include "gal-a11y-e-table-item-factory.h"
49 #include "gal-a11y-e-table-item.h"
50
51 G_DEFINE_TYPE (
52 ETableItem,
53 e_table_item,
54 GNOME_TYPE_CANVAS_ITEM)
55
56 #define FOCUSED_BORDER 2
57
58 #define d(x)
59
60 #if d(!)0
61 #define e_table_item_leave_edit_(x) (e_table_item_leave_edit((x)), g_print ("%s: e_table_item_leave_edit\n", G_STRFUNC))
62 #else
63 #define e_table_item_leave_edit_(x) (e_table_item_leave_edit((x)))
64 #endif
65
66 #define E_TABLE_ITEM_GET_PRIVATE(obj) \
67 (G_TYPE_INSTANCE_GET_PRIVATE \
68 ((obj), E_TYPE_TABLE_ITEM, ETableItemPrivate))
69
70 typedef struct _ETableItemPrivate ETableItemPrivate;
71
72 struct _ETableItemPrivate {
73 GSource *show_cursor_delay_source;
74 };
75
76 static void eti_check_cursor_bounds (ETableItem *eti);
77 static void eti_cancel_drag_due_to_model_change (ETableItem *eti);
78
79 /* FIXME: Do an analysis of which cell functions are needed before
80 * realize and make sure that all of them are doable by all the cells
81 * and that all of the others are only done after realization. */
82
83 enum {
84 CURSOR_CHANGE,
85 CURSOR_ACTIVATED,
86 DOUBLE_CLICK,
87 RIGHT_CLICK,
88 CLICK,
89 KEY_PRESS,
90 START_DRAG,
91 STYLE_UPDATED,
92 SELECTION_MODEL_REMOVED,
93 SELECTION_MODEL_ADDED,
94 GET_BG_COLOR,
95 LAST_SIGNAL
96 };
97
98 static guint eti_signals[LAST_SIGNAL] = { 0, };
99
100 enum {
101 PROP_0,
102 PROP_TABLE_HEADER,
103 PROP_TABLE_MODEL,
104 PROP_SELECTION_MODEL,
105 PROP_TABLE_ALTERNATING_ROW_COLORS,
106 PROP_TABLE_HORIZONTAL_DRAW_GRID,
107 PROP_TABLE_VERTICAL_DRAW_GRID,
108 PROP_TABLE_DRAW_FOCUS,
109 PROP_CURSOR_MODE,
110 PROP_LENGTH_THRESHOLD,
111 PROP_CURSOR_ROW,
112 PROP_UNIFORM_ROW_HEIGHT,
113 PROP_IS_EDITING,
114
115 PROP_MINIMUM_WIDTH,
116 PROP_WIDTH,
117 PROP_HEIGHT
118 };
119
120 #define DOUBLE_CLICK_TIME 250
121 #define TRIPLE_CLICK_TIME 500
122
123 static gint eti_get_height (ETableItem *eti);
124 static gint eti_row_height (ETableItem *eti, gint row);
125 static void e_table_item_focus (ETableItem *eti, gint col, gint row, GdkModifierType state);
126 static void eti_cursor_change (ESelectionModel *selection, gint row, gint col, ETableItem *eti);
127 static void eti_cursor_activated (ESelectionModel *selection, gint row, gint col, ETableItem *eti);
128 static void eti_selection_change (ESelectionModel *selection, ETableItem *eti);
129 static void eti_selection_row_change (ESelectionModel *selection, gint row, ETableItem *eti);
130 static void e_table_item_redraw_row (ETableItem *eti, gint row);
131
132 #define ETI_SINGLE_ROW_HEIGHT(eti) ((eti)->uniform_row_height_cache != -1 ? (eti)->uniform_row_height_cache : eti_row_height((eti), -1))
133 #define ETI_MULTIPLE_ROW_HEIGHT(eti,row) ((eti)->height_cache && (eti)->height_cache[(row)] != -1 ? (eti)->height_cache[(row)] : eti_row_height((eti),(row)))
134 #define ETI_ROW_HEIGHT(eti,row) ((eti)->uniform_row_height ? ETI_SINGLE_ROW_HEIGHT ((eti)) : ETI_MULTIPLE_ROW_HEIGHT((eti),(row)))
135
136 /* tweak_hsv is a really tweaky function. it modifies its first argument, which
137 * should be the color you want tweaked. delta_h, delta_s and delta_v specify
138 * how much you want their respective channels modified (and in what direction).
139 * if it can't do the specified modification, it does it in the oppositon direction */
140 static void
e_hsv_tweak(GdkRGBA * rgba,gdouble delta_h,gdouble delta_s,gdouble delta_v)141 e_hsv_tweak (GdkRGBA *rgba,
142 gdouble delta_h,
143 gdouble delta_s,
144 gdouble delta_v)
145 {
146 gdouble h, s, v, r, g, b;
147
148 r = rgba->red;
149 g = rgba->green;
150 b = rgba->blue;
151
152 gtk_rgb_to_hsv (r, g, b, &h, &s, &v);
153
154 if (h + delta_h < 0) {
155 h -= delta_h;
156 } else {
157 h += delta_h;
158 }
159
160 if (s + delta_s < 0) {
161 s -= delta_s;
162 } else {
163 s += delta_s;
164 }
165
166 if (v + delta_v < 0) {
167 v -= delta_v;
168 } else {
169 v += delta_v;
170 }
171
172 gtk_hsv_to_rgb (h, s, v, &r, &g, &b);
173
174 rgba->red = r;
175 rgba->green = g;
176 rgba->blue = b;
177 }
178
179 inline static gint
model_to_view_row(ETableItem * eti,gint row)180 model_to_view_row (ETableItem *eti,
181 gint row)
182 {
183 if (row == -1)
184 return -1;
185 if (eti->uses_source_model) {
186 gint model_row;
187
188 model_row = e_table_subset_view_to_model_row (
189 E_TABLE_SUBSET (eti->table_model), eti->row_guess);
190 if (model_row >= 0 && model_row == row)
191 return eti->row_guess;
192
193 return e_table_subset_model_to_view_row (
194 E_TABLE_SUBSET (eti->table_model), row);
195 } else
196 return row;
197 }
198
199 inline static gint
view_to_model_row(ETableItem * eti,gint row)200 view_to_model_row (ETableItem *eti,
201 gint row)
202 {
203 if (eti->uses_source_model) {
204 gint model_row;
205
206 model_row = e_table_subset_view_to_model_row (
207 E_TABLE_SUBSET (eti->table_model), row);
208
209 if (model_row >= 0)
210 eti->row_guess = row;
211
212 return model_row;
213 } else
214 return row;
215 }
216
217 inline static gint
model_to_view_col(ETableItem * eti,gint model_col)218 model_to_view_col (ETableItem *eti,
219 gint model_col)
220 {
221 gint i;
222 if (model_col == -1)
223 return -1;
224 for (i = 0; i < eti->cols; i++) {
225 ETableCol *ecol = e_table_header_get_column (eti->header, i);
226 if (ecol->spec->model_col == model_col)
227 return i;
228 }
229 return -1;
230 }
231
232 inline static gint
view_to_model_col(ETableItem * eti,gint view_col)233 view_to_model_col (ETableItem *eti,
234 gint view_col)
235 {
236 ETableCol *ecol = e_table_header_get_column (eti->header, view_col);
237
238 return (ecol != NULL) ? ecol->spec->model_col : -1;
239 }
240
241 static void
grab_cancelled(ECanvas * canvas,GnomeCanvasItem * item,gpointer data)242 grab_cancelled (ECanvas *canvas,
243 GnomeCanvasItem *item,
244 gpointer data)
245 {
246 ETableItem *eti = data;
247
248 eti->grab_cancelled = TRUE;
249 }
250
251 inline static void
eti_grab(ETableItem * eti,GdkDevice * device,guint32 time)252 eti_grab (ETableItem *eti,
253 GdkDevice *device,
254 guint32 time)
255 {
256 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
257 d (g_print ("%s: time: %d\n", G_STRFUNC, time));
258 if (eti->grabbed_count == 0) {
259 GdkGrabStatus grab_status;
260
261 eti->gtk_grabbed = FALSE;
262 eti->grab_cancelled = FALSE;
263
264 grab_status = e_canvas_item_grab (
265 E_CANVAS (item->canvas),
266 item,
267 GDK_BUTTON1_MOTION_MASK |
268 GDK_BUTTON2_MOTION_MASK |
269 GDK_BUTTON3_MOTION_MASK |
270 GDK_POINTER_MOTION_MASK |
271 GDK_BUTTON_PRESS_MASK |
272 GDK_BUTTON_RELEASE_MASK,
273 NULL,
274 device, time,
275 grab_cancelled,
276 eti);
277
278 if (grab_status != GDK_GRAB_SUCCESS) {
279 d (g_print ("%s: gtk_grab_add\n", G_STRFUNC));
280 gtk_grab_add (GTK_WIDGET (item->canvas));
281 eti->gtk_grabbed = TRUE;
282 }
283 }
284 eti->grabbed_count++;
285 }
286
287 inline static void
eti_ungrab(ETableItem * eti,guint32 time)288 eti_ungrab (ETableItem *eti,
289 guint32 time)
290 {
291 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
292 gboolean was_grabbed;
293
294 d (g_print ("%s: time: %d\n", G_STRFUNC, time));
295
296 was_grabbed = eti->grabbed_count > 0;
297 if (was_grabbed)
298 eti->grabbed_count--;
299
300 if (eti->grabbed_count == 0) {
301 if (eti->grab_cancelled) {
302 eti->grab_cancelled = FALSE;
303 } else {
304 if (eti->gtk_grabbed) {
305 d (g_print ("%s: gtk_grab_remove\n", G_STRFUNC));
306 gtk_grab_remove (GTK_WIDGET (item->canvas));
307 eti->gtk_grabbed = FALSE;
308 }
309 if (was_grabbed)
310 gnome_canvas_item_ungrab (item, time);
311 eti->grabbed_col = -1;
312 eti->grabbed_row = -1;
313 }
314 }
315 }
316
317 inline static gboolean
eti_editing(ETableItem * eti)318 eti_editing (ETableItem *eti)
319 {
320 d (g_print ("%s: %s\n", G_STRFUNC, (eti->editing_col == -1) ? "false":"true"));
321
322 if (eti->editing_col == -1)
323 return FALSE;
324 else
325 return TRUE;
326 }
327
328 static void
eti_get_cell_background_color(ETableItem * eti,gint row,gint col,gboolean selected,GdkRGBA * background)329 eti_get_cell_background_color (ETableItem *eti,
330 gint row,
331 gint col,
332 gboolean selected,
333 GdkRGBA *background)
334 {
335 ECellView *ecell_view = eti->cell_views[col];
336 GtkWidget *canvas;
337 gboolean was_set = FALSE;
338 gchar *color_spec = NULL;
339
340 canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (eti)->canvas);
341
342 g_signal_emit (eti, eti_signals[GET_BG_COLOR], 0, row, col, background, &was_set);
343
344 if (!was_set) {
345 color_spec = e_cell_get_bg_color (ecell_view, row);
346
347 if (color_spec != NULL) {
348 GdkRGBA bg;
349
350 if (gdk_rgba_parse (&bg, color_spec)) {
351 *background = bg;
352 was_set = TRUE;
353 }
354 }
355
356 g_free (color_spec);
357 }
358
359 if (!was_set) {
360 if (selected) {
361 if (gtk_widget_has_focus (canvas))
362 e_utils_get_theme_color (canvas, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, background);
363 else
364 e_utils_get_theme_color (canvas, "theme_unfocused_selected_bg_color,theme_selected_bg_color", E_UTILS_DEFAULT_THEME_UNFOCUSED_SELECTED_BG_COLOR, background);
365 } else {
366 e_utils_get_theme_color (canvas, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, background);
367 }
368 }
369
370 if (eti->alternating_row_colors) {
371 if (row % 2) {
372
373 } else {
374 e_hsv_tweak (background, 0.0f, 0.0f, -0.07f);
375 }
376 }
377 }
378
379 static void
eti_free_save_state(ETableItem * eti)380 eti_free_save_state (ETableItem *eti)
381 {
382 if (eti->save_row == -1 ||
383 !eti->cell_views_realized)
384 return;
385
386 e_cell_free_state (
387 eti->cell_views[eti->save_col], view_to_model_col (eti, eti->save_col),
388 eti->save_col, eti->save_row, eti->save_state);
389 eti->save_row = -1;
390 eti->save_col = -1;
391 eti->save_state = NULL;
392 }
393
394 /*
395 * During realization, we have to invoke the per-ecell realize routine
396 * (On our current setup, we have one e-cell per column.
397 *
398 * We might want to optimize this to only realize the unique e-cells:
399 * ie, a strings-only table, uses the same e-cell for every column, and
400 * we might want to avoid realizing each e-cell.
401 */
402 static void
eti_realize_cell_views(ETableItem * eti)403 eti_realize_cell_views (ETableItem *eti)
404 {
405 GnomeCanvasItem *item;
406 gint i;
407
408 item = GNOME_CANVAS_ITEM (eti);
409
410 if (eti->cell_views_realized)
411 return;
412
413 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
414 return;
415
416 for (i = 0; i < eti->n_cells; i++)
417 e_cell_realize (eti->cell_views[i]);
418 eti->cell_views_realized = 1;
419 }
420
421 static void
eti_attach_cell_views(ETableItem * eti)422 eti_attach_cell_views (ETableItem *eti)
423 {
424 gint i;
425
426 g_return_if_fail (eti->header);
427 g_return_if_fail (eti->table_model);
428
429 /* this is just c&p from model pre change, but it fixes things */
430 eti_cancel_drag_due_to_model_change (eti);
431 eti_check_cursor_bounds (eti);
432 if (eti_editing (eti))
433 e_table_item_leave_edit_(eti);
434 eti->motion_row = -1;
435 eti->motion_col = -1;
436
437 /*
438 * Now realize the various ECells
439 */
440 eti->n_cells = eti->cols;
441 eti->cell_views = g_new (ECellView *, eti->n_cells);
442
443 for (i = 0; i < eti->n_cells; i++) {
444 ETableCol *ecol = e_table_header_get_column (eti->header, i);
445
446 eti->cell_views[i] = e_cell_new_view (ecol->ecell, eti->table_model, eti);
447 }
448
449 eti->needs_compute_height = 1;
450 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
451 eti->needs_redraw = 1;
452 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
453 }
454
455 /*
456 * During unrealization: we invoke every e-cell (one per column in the current
457 * setup) to dispose all X resources allocated
458 */
459 static void
eti_unrealize_cell_views(ETableItem * eti)460 eti_unrealize_cell_views (ETableItem *eti)
461 {
462 gint i;
463
464 if (eti->cell_views_realized == 0)
465 return;
466
467 eti_free_save_state (eti);
468
469 for (i = 0; i < eti->n_cells; i++)
470 e_cell_unrealize (eti->cell_views[i]);
471 eti->cell_views_realized = 0;
472 }
473
474 static void
eti_detach_cell_views(ETableItem * eti)475 eti_detach_cell_views (ETableItem *eti)
476 {
477 gint i;
478
479 eti_free_save_state (eti);
480
481 for (i = 0; i < eti->n_cells; i++) {
482 e_cell_kill_view (eti->cell_views[i]);
483 eti->cell_views[i] = NULL;
484 }
485
486 g_free (eti->cell_views);
487 eti->cell_views = NULL;
488 eti->n_cells = 0;
489 }
490
491 static void
eti_bounds(GnomeCanvasItem * item,gdouble * x1,gdouble * y1,gdouble * x2,gdouble * y2)492 eti_bounds (GnomeCanvasItem *item,
493 gdouble *x1,
494 gdouble *y1,
495 gdouble *x2,
496 gdouble *y2)
497 {
498 cairo_matrix_t i2c;
499 ETableItem *eti = E_TABLE_ITEM (item);
500
501 /* Wrong BBox's are the source of redraw nightmares */
502
503 *x1 = 0;
504 *y1 = 0;
505 *x2 = eti->width;
506 *y2 = eti->height;
507
508 gnome_canvas_item_i2c_matrix (GNOME_CANVAS_ITEM (eti), &i2c);
509 gnome_canvas_matrix_transform_rect (&i2c, x1, y1, x2, y2);
510 }
511
512 static void
eti_reflow(GnomeCanvasItem * item,gint flags)513 eti_reflow (GnomeCanvasItem *item,
514 gint flags)
515 {
516 ETableItem *eti = E_TABLE_ITEM (item);
517
518 if (eti->needs_compute_height) {
519 gint new_height = eti_get_height (eti);
520
521 if (new_height != eti->height) {
522 eti->height = new_height;
523 e_canvas_item_request_parent_reflow (GNOME_CANVAS_ITEM (eti));
524 eti->needs_redraw = 1;
525 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
526 }
527 eti->needs_compute_height = 0;
528 }
529 if (eti->needs_compute_width) {
530 gint new_width = e_table_header_total_width (eti->header);
531 if (new_width != eti->width) {
532 eti->width = new_width;
533 e_canvas_item_request_parent_reflow (GNOME_CANVAS_ITEM (eti));
534 eti->needs_redraw = 1;
535 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
536 }
537 eti->needs_compute_width = 0;
538 }
539 }
540
541 /*
542 * GnomeCanvasItem::update method
543 */
544 static void
eti_update(GnomeCanvasItem * item,const cairo_matrix_t * i2c,gint flags)545 eti_update (GnomeCanvasItem *item,
546 const cairo_matrix_t *i2c,
547 gint flags)
548 {
549 ETableItem *eti = E_TABLE_ITEM (item);
550 gdouble x1, x2, y1, y2;
551
552 if (GNOME_CANVAS_ITEM_CLASS (e_table_item_parent_class)->update)
553 (*GNOME_CANVAS_ITEM_CLASS (e_table_item_parent_class)->update)(item, i2c, flags);
554
555 x1 = item->x1;
556 y1 = item->y1;
557 x2 = item->x2;
558 y2 = item->y2;
559
560 eti_bounds (item, &item->x1, &item->y1, &item->x2, &item->y2);
561 if (item->x1 != x1 ||
562 item->y1 != y1 ||
563 item->x2 != x2 ||
564 item->y2 != y2) {
565 gnome_canvas_request_redraw (item->canvas, x1, y1, x2, y2);
566 eti->needs_redraw = 1;
567 }
568
569 if (eti->needs_redraw) {
570 gnome_canvas_request_redraw (
571 item->canvas, item->x1, item->y1,
572 item->x2, item->y2);
573 eti->needs_redraw = 0;
574 }
575 }
576
577 /*
578 * eti_remove_table_model:
579 *
580 * Invoked to release the table model associated with this ETableItem
581 */
582 static void
eti_remove_table_model(ETableItem * eti)583 eti_remove_table_model (ETableItem *eti)
584 {
585 if (!eti->table_model)
586 return;
587
588 g_signal_handler_disconnect (
589 eti->table_model,
590 eti->table_model_pre_change_id);
591 g_signal_handler_disconnect (
592 eti->table_model,
593 eti->table_model_no_change_id);
594 g_signal_handler_disconnect (
595 eti->table_model,
596 eti->table_model_change_id);
597 g_signal_handler_disconnect (
598 eti->table_model,
599 eti->table_model_row_change_id);
600 g_signal_handler_disconnect (
601 eti->table_model,
602 eti->table_model_cell_change_id);
603 g_signal_handler_disconnect (
604 eti->table_model,
605 eti->table_model_rows_inserted_id);
606 g_signal_handler_disconnect (
607 eti->table_model,
608 eti->table_model_rows_deleted_id);
609 g_object_unref (eti->table_model);
610 if (eti->source_model)
611 g_object_unref (eti->source_model);
612
613 eti->table_model_pre_change_id = 0;
614 eti->table_model_no_change_id = 0;
615 eti->table_model_change_id = 0;
616 eti->table_model_row_change_id = 0;
617 eti->table_model_cell_change_id = 0;
618 eti->table_model_rows_inserted_id = 0;
619 eti->table_model_rows_deleted_id = 0;
620 eti->table_model = NULL;
621 eti->source_model = NULL;
622 eti->uses_source_model = 0;
623 }
624
625 /*
626 * eti_remove_table_model:
627 *
628 * Invoked to release the table model associated with this ETableItem
629 */
630 static void
eti_remove_selection_model(ETableItem * eti)631 eti_remove_selection_model (ETableItem *eti)
632 {
633 if (!eti->selection)
634 return;
635
636 g_signal_handler_disconnect (
637 eti->selection,
638 eti->selection_change_id);
639 g_signal_handler_disconnect (
640 eti->selection,
641 eti->selection_row_change_id);
642 g_signal_handler_disconnect (
643 eti->selection,
644 eti->cursor_change_id);
645 g_signal_handler_disconnect (
646 eti->selection,
647 eti->cursor_activated_id);
648 g_object_unref (eti->selection);
649
650 eti->selection_change_id = 0;
651 eti->selection_row_change_id = 0;
652 eti->cursor_activated_id = 0;
653 eti->selection = NULL;
654 }
655
656 /*
657 * eti_remove_header_model:
658 *
659 * Invoked to release the header model associated with this ETableItem
660 */
661 static void
eti_remove_header_model(ETableItem * eti)662 eti_remove_header_model (ETableItem *eti)
663 {
664 if (!eti->header)
665 return;
666
667 g_signal_handler_disconnect (
668 eti->header,
669 eti->header_structure_change_id);
670 g_signal_handler_disconnect (
671 eti->header,
672 eti->header_dim_change_id);
673 g_signal_handler_disconnect (
674 eti->header,
675 eti->header_request_width_id);
676
677 if (eti->cell_views) {
678 eti_unrealize_cell_views (eti);
679 eti_detach_cell_views (eti);
680 }
681 g_object_unref (eti->header);
682
683 eti->header_structure_change_id = 0;
684 eti->header_dim_change_id = 0;
685 eti->header_request_width_id = 0;
686 eti->header = NULL;
687 }
688
689 /*
690 * eti_row_height_real:
691 *
692 * Returns the height used by row @row. This does not include the one-pixel
693 * used as a separator between rows
694 */
695 static gint
eti_row_height_real(ETableItem * eti,gint row)696 eti_row_height_real (ETableItem *eti,
697 gint row)
698 {
699 const gint cols = e_table_header_count (eti->header);
700 gint col;
701 gint h, max_h;
702
703 g_return_val_if_fail (cols == 0 || eti->cell_views, 0);
704
705 max_h = 0;
706
707 for (col = 0; col < cols; col++) {
708 h = e_cell_height (eti->cell_views[col], view_to_model_col (eti, col), col, row);
709
710 if (h > max_h)
711 max_h = h;
712 }
713 return max_h;
714 }
715
716 static void
confirm_height_cache(ETableItem * eti)717 confirm_height_cache (ETableItem *eti)
718 {
719 gint i;
720
721 if (eti->uniform_row_height || eti->height_cache)
722 return;
723 eti->height_cache = g_new (int, eti->rows);
724 for (i = 0; i < eti->rows; i++) {
725 eti->height_cache[i] = -1;
726 }
727 }
728
729 static gboolean
height_cache_idle(ETableItem * eti)730 height_cache_idle (ETableItem *eti)
731 {
732 gint changed = 0;
733 gint i;
734 confirm_height_cache (eti);
735 for (i = eti->height_cache_idle_count; i < eti->rows; i++) {
736 if (eti->height_cache[i] == -1) {
737 eti_row_height (eti, i);
738 changed++;
739 if (changed >= 20)
740 break;
741 }
742 }
743 if (changed >= 20) {
744 eti->height_cache_idle_count = i;
745 return TRUE;
746 }
747 eti->height_cache_idle_id = 0;
748 return FALSE;
749 }
750
751 static void
free_height_cache(ETableItem * eti)752 free_height_cache (ETableItem *eti)
753 {
754 GnomeCanvasItem *item;
755
756 item = GNOME_CANVAS_ITEM (eti);
757
758 if (item->flags & GNOME_CANVAS_ITEM_REALIZED) {
759 g_clear_pointer (&eti->height_cache, g_free);
760 eti->height_cache_idle_count = 0;
761 eti->uniform_row_height_cache = -1;
762
763 if (eti->uniform_row_height && eti->height_cache_idle_id != 0) {
764 g_source_remove (eti->height_cache_idle_id);
765 eti->height_cache_idle_id = 0;
766 }
767
768 if ((!eti->uniform_row_height) && eti->height_cache_idle_id == 0)
769 eti->height_cache_idle_id = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) height_cache_idle, eti, NULL);
770 }
771 }
772
773 static void
calculate_height_cache(ETableItem * eti)774 calculate_height_cache (ETableItem *eti)
775 {
776 free_height_cache (eti);
777 confirm_height_cache (eti);
778 }
779
780 /*
781 * eti_row_height:
782 *
783 * Returns the height used by row @row. This does not include the one-pixel
784 * used as a separator between rows
785 */
786 static gint
eti_row_height(ETableItem * eti,gint row)787 eti_row_height (ETableItem *eti,
788 gint row)
789 {
790 if (eti->uniform_row_height) {
791 eti->uniform_row_height_cache = eti_row_height_real (eti, -1);
792 return eti->uniform_row_height_cache;
793 } else {
794 if (!eti->height_cache) {
795 calculate_height_cache (eti);
796 }
797 if (eti->height_cache[row] == -1) {
798 eti->height_cache[row] = eti_row_height_real (eti, row);
799 if (row > 0 &&
800 eti->length_threshold != -1 &&
801 eti->rows > eti->length_threshold &&
802 eti->height_cache[row] != eti_row_height (eti, 0)) {
803 eti->needs_compute_height = 1;
804 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
805 }
806 }
807 return eti->height_cache[row];
808 }
809 }
810
811 /*
812 * eti_get_height:
813 *
814 * Returns the height of the ETableItem.
815 *
816 * The ETableItem might compute the whole height by asking every row its
817 * size. There is a special mode (designed to work when there are too
818 * many rows in the table that performing the previous step could take
819 * too long) set by the ETableItem->length_threshold that would determine
820 * when the height is computed by using the first row as the size for
821 * every other row in the ETableItem.
822 */
823 static gint
eti_get_height(ETableItem * eti)824 eti_get_height (ETableItem *eti)
825 {
826 const gint rows = eti->rows;
827 gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
828
829 if (rows == 0)
830 return 0;
831
832 if (eti->uniform_row_height) {
833 gint row_height = ETI_ROW_HEIGHT (eti, -1);
834 return ((row_height + height_extra) * rows + height_extra);
835 } else {
836 gint height;
837 gint row;
838 if (eti->length_threshold != -1) {
839 if (rows > eti->length_threshold) {
840 gint row_height = ETI_ROW_HEIGHT (eti, 0);
841 if (eti->height_cache) {
842 height = 0;
843 for (row = 0; row < rows; row++) {
844 if (eti->height_cache[row] == -1) {
845 height += (row_height + height_extra) * (rows - row);
846 break;
847 }
848 else
849 height += eti->height_cache[row] + height_extra;
850 }
851 } else
852 height = (ETI_ROW_HEIGHT (eti, 0) + height_extra) * rows;
853
854 /*
855 * 1 pixel at the top
856 */
857 return height + height_extra;
858 }
859 }
860
861 height = height_extra;
862 for (row = 0; row < rows; row++)
863 height += ETI_ROW_HEIGHT (eti, row) + height_extra;
864
865 return height;
866 }
867 }
868
869 static void
eti_item_region_redraw(ETableItem * eti,gint x0,gint y0,gint x1,gint y1)870 eti_item_region_redraw (ETableItem *eti,
871 gint x0,
872 gint y0,
873 gint x1,
874 gint y1)
875 {
876 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
877 gdouble dx1, dy1, dx2, dy2;
878 cairo_matrix_t i2c;
879
880 dx1 = x0;
881 dy1 = y0;
882 dx2 = x1;
883 dy2 = y1;
884
885 gnome_canvas_item_i2c_matrix (item, &i2c);
886 gnome_canvas_matrix_transform_rect (&i2c, &dx1, &dy1, &dx2, &dy2);
887
888 gnome_canvas_request_redraw (item->canvas, floor (dx1), floor (dy1), ceil (dx2), ceil (dy2));
889 }
890
891 /*
892 * Computes the distance between @start_row and @end_row in pixels
893 */
894 gint
e_table_item_row_diff(ETableItem * eti,gint start_row,gint end_row)895 e_table_item_row_diff (ETableItem *eti,
896 gint start_row,
897 gint end_row)
898 {
899 gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
900
901 if (start_row < 0)
902 start_row = 0;
903 if (end_row > eti->rows)
904 end_row = eti->rows;
905
906 if (eti->uniform_row_height) {
907 return ((end_row - start_row) * (ETI_ROW_HEIGHT (eti, -1) + height_extra));
908 } else {
909 gint row, total;
910 total = 0;
911 for (row = start_row; row < end_row; row++)
912 total += ETI_ROW_HEIGHT (eti, row) + height_extra;
913
914 return total;
915 }
916 }
917
918 static void
eti_get_region(ETableItem * eti,gint start_col,gint start_row,gint end_col,gint end_row,gint * x1p,gint * y1p,gint * x2p,gint * y2p)919 eti_get_region (ETableItem *eti,
920 gint start_col,
921 gint start_row,
922 gint end_col,
923 gint end_row,
924 gint *x1p,
925 gint *y1p,
926 gint *x2p,
927 gint *y2p)
928 {
929 gint x1, y1, x2, y2;
930
931 x1 = e_table_header_col_diff (eti->header, 0, start_col);
932 y1 = e_table_item_row_diff (eti, 0, start_row);
933 x2 = x1 + e_table_header_col_diff (eti->header, start_col, end_col + 1);
934 y2 = y1 + e_table_item_row_diff (eti, start_row, end_row + 1);
935 if (x1p)
936 *x1p = x1;
937 if (y1p)
938 *y1p = y1;
939 if (x2p)
940 *x2p = x2;
941 if (y2p)
942 *y2p = y2;
943 }
944
945 /*
946 * eti_request_region_redraw:
947 *
948 * Request a canvas redraw on the range (start_col, start_row) to (end_col, end_row).
949 * This is inclusive (ie, you can use: 0,0-0,0 to redraw the first cell).
950 *
951 * The @border argument is a number of pixels around the region that should also be queued
952 * for redraw. This is typically used by the focus routines to queue a redraw for the
953 * border as well.
954 */
955 static void
eti_request_region_redraw(ETableItem * eti,gint start_col,gint start_row,gint end_col,gint end_row,gint border)956 eti_request_region_redraw (ETableItem *eti,
957 gint start_col,
958 gint start_row,
959 gint end_col,
960 gint end_row,
961 gint border)
962 {
963 gint x1, y1, x2, y2;
964
965 if (eti->rows > 0) {
966
967 eti_get_region (
968 eti,
969 start_col, start_row,
970 end_col, end_row,
971 &x1, &y1, &x2, &y2);
972
973 eti_item_region_redraw (
974 eti,
975 x1 - border,
976 y1 - border,
977 x2 + 1 + border,
978 y2 + 1 + border);
979 }
980 }
981
982 /*
983 * eti_request_region_show
984 *
985 * Request a canvas show on the range (start_col, start_row) to (end_col, end_row).
986 * This is inclusive (ie, you can use: 0,0-0,0 to show the first cell).
987 */
988 static void
eti_request_region_show(ETableItem * eti,gint start_col,gint start_row,gint end_col,gint end_row,gint delay)989 eti_request_region_show (ETableItem *eti,
990 gint start_col,
991 gint start_row,
992 gint end_col,
993 gint end_row,
994 gint delay)
995 {
996 ETableItemPrivate *priv = E_TABLE_ITEM_GET_PRIVATE (eti);
997 gint x1, y1, x2, y2;
998
999 if (priv->show_cursor_delay_source) {
1000 g_source_destroy (priv->show_cursor_delay_source);
1001 g_source_unref (priv->show_cursor_delay_source);
1002 priv->show_cursor_delay_source = NULL;
1003 }
1004
1005 eti_get_region (
1006 eti,
1007 start_col, start_row,
1008 end_col, end_row,
1009 &x1, &y1, &x2, &y2);
1010
1011 if (delay)
1012 priv->show_cursor_delay_source = e_canvas_item_show_area_delayed_ex (
1013 GNOME_CANVAS_ITEM (eti), x1, y1, x2, y2, delay);
1014 else
1015 e_canvas_item_show_area (
1016 GNOME_CANVAS_ITEM (eti), x1, y1, x2, y2);
1017 }
1018
1019 static void
eti_show_cursor(ETableItem * eti,gint delay)1020 eti_show_cursor (ETableItem *eti,
1021 gint delay)
1022 {
1023 GnomeCanvasItem *item;
1024 gint cursor_row;
1025
1026 item = GNOME_CANVAS_ITEM (eti);
1027
1028 if (!((item->flags & GNOME_CANVAS_ITEM_REALIZED) && eti->cell_views_realized))
1029 return;
1030
1031 if (eti->frozen_count > 0) {
1032 eti->queue_show_cursor = TRUE;
1033 return;
1034 }
1035
1036 #if 0
1037 g_object_get (
1038 eti->selection,
1039 "cursor_row", &cursor_row,
1040 NULL);
1041 #else
1042 cursor_row = e_selection_model_cursor_row (eti->selection);
1043 #endif
1044
1045 d (g_print ("%s: cursor row: %d\n", G_STRFUNC, cursor_row));
1046
1047 if (cursor_row != -1) {
1048 cursor_row = model_to_view_row (eti, cursor_row);
1049 eti_request_region_show (
1050 eti,
1051 0, cursor_row, eti->cols - 1, cursor_row,
1052 delay);
1053 }
1054 }
1055
1056 static void
eti_check_cursor_bounds(ETableItem * eti)1057 eti_check_cursor_bounds (ETableItem *eti)
1058 {
1059 GnomeCanvasItem *item;
1060 gint x1, y1, x2, y2;
1061 gint cursor_row;
1062
1063 item = GNOME_CANVAS_ITEM (eti);
1064
1065 if (!((item->flags & GNOME_CANVAS_ITEM_REALIZED) && eti->cell_views_realized))
1066 return;
1067
1068 if (eti->frozen_count > 0) {
1069 return;
1070 }
1071
1072 g_object_get (
1073 eti->selection,
1074 "cursor_row", &cursor_row,
1075 NULL);
1076
1077 if (cursor_row == -1) {
1078 eti->cursor_x1 = -1;
1079 eti->cursor_y1 = -1;
1080 eti->cursor_x2 = -1;
1081 eti->cursor_y2 = -1;
1082 eti->cursor_on_screen = TRUE;
1083 return;
1084 }
1085
1086 d (g_print ("%s: model cursor row: %d\n", G_STRFUNC, cursor_row));
1087
1088 cursor_row = model_to_view_row (eti, cursor_row);
1089
1090 d (g_print ("%s: cursor row: %d\n", G_STRFUNC, cursor_row));
1091
1092 eti_get_region (
1093 eti,
1094 0, cursor_row, eti->cols - 1, cursor_row,
1095 &x1, &y1, &x2, &y2);
1096 eti->cursor_x1 = x1;
1097 eti->cursor_y1 = y1;
1098 eti->cursor_x2 = x2;
1099 eti->cursor_y2 = y2;
1100 eti->cursor_on_screen = e_canvas_item_area_shown (GNOME_CANVAS_ITEM (eti), x1, y1, x2, y2);
1101
1102 d (g_print ("%s: cursor on screen: %s\n", G_STRFUNC, eti->cursor_on_screen ? "TRUE" : "FALSE"));
1103 }
1104
1105 static void
eti_maybe_show_cursor(ETableItem * eti,gint delay)1106 eti_maybe_show_cursor (ETableItem *eti,
1107 gint delay)
1108 {
1109 d (g_print ("%s: cursor on screen: %s\n", G_STRFUNC, eti->cursor_on_screen ? "TRUE" : "FALSE"));
1110 if (eti->cursor_on_screen)
1111 eti_show_cursor (eti, delay);
1112 eti_check_cursor_bounds (eti);
1113 }
1114
1115 static gboolean
eti_idle_show_cursor_cb(gpointer data)1116 eti_idle_show_cursor_cb (gpointer data)
1117 {
1118 ETableItem *eti = data;
1119
1120 if (eti->selection) {
1121 eti_show_cursor (eti, 0);
1122 eti_check_cursor_bounds (eti);
1123 }
1124
1125 eti->cursor_idle_id = 0;
1126 g_object_unref (eti);
1127 return FALSE;
1128 }
1129
1130 static void
eti_idle_maybe_show_cursor(ETableItem * eti)1131 eti_idle_maybe_show_cursor (ETableItem *eti)
1132 {
1133 d (g_print ("%s: cursor on screen: %s\n", G_STRFUNC, eti->cursor_on_screen ? "TRUE" : "FALSE"));
1134 if (eti->cursor_on_screen) {
1135 g_object_ref (eti);
1136 if (!eti->cursor_idle_id)
1137 eti->cursor_idle_id = g_idle_add (eti_idle_show_cursor_cb, eti);
1138 }
1139 }
1140
1141 static void
eti_cancel_drag_due_to_model_change(ETableItem * eti)1142 eti_cancel_drag_due_to_model_change (ETableItem *eti)
1143 {
1144 if (eti->maybe_in_drag) {
1145 eti->maybe_in_drag = FALSE;
1146 if (!eti->maybe_did_something)
1147 e_selection_model_do_something (E_SELECTION_MODEL (eti->selection), eti->drag_row, eti->drag_col, eti->drag_state);
1148 }
1149 if (eti->in_drag) {
1150 eti->in_drag = FALSE;
1151 }
1152 }
1153
1154 static void
eti_freeze(ETableItem * eti)1155 eti_freeze (ETableItem *eti)
1156 {
1157 eti->frozen_count++;
1158 d (g_print ("%s: %d\n", G_STRFUNC, eti->frozen_count));
1159 }
1160
1161 static void
eti_unfreeze(ETableItem * eti)1162 eti_unfreeze (ETableItem *eti)
1163 {
1164 if (eti->frozen_count <= 0)
1165 return;
1166
1167 eti->frozen_count--;
1168 d (g_print ("%s: %d\n", G_STRFUNC, eti->frozen_count));
1169 if (eti->frozen_count == 0 && eti->queue_show_cursor) {
1170 eti_show_cursor (eti, 0);
1171 eti_check_cursor_bounds (eti);
1172 eti->queue_show_cursor = FALSE;
1173 }
1174 }
1175
1176 void
e_table_item_freeze(ETableItem * eti)1177 e_table_item_freeze (ETableItem *eti)
1178 {
1179 if (!eti)
1180 return;
1181
1182 eti_freeze (eti);
1183 }
1184
1185 void
e_table_item_thaw(ETableItem * eti)1186 e_table_item_thaw (ETableItem *eti)
1187 {
1188 if (!eti)
1189 return;
1190
1191 eti_unfreeze (eti);
1192 }
1193
1194 /*
1195 * Callback routine: invoked before the ETableModel suffers a change
1196 */
1197 static void
eti_table_model_pre_change(ETableModel * table_model,ETableItem * eti)1198 eti_table_model_pre_change (ETableModel *table_model,
1199 ETableItem *eti)
1200 {
1201 eti_cancel_drag_due_to_model_change (eti);
1202 eti_check_cursor_bounds (eti);
1203 if (eti_editing (eti))
1204 e_table_item_leave_edit_(eti);
1205 eti->motion_row = -1;
1206 eti->motion_col = -1;
1207 eti_freeze (eti);
1208 }
1209
1210 /*
1211 * Callback routine: invoked when the ETableModel has not suffered a change
1212 */
1213 static void
eti_table_model_no_change(ETableModel * table_model,ETableItem * eti)1214 eti_table_model_no_change (ETableModel *table_model,
1215 ETableItem *eti)
1216 {
1217 eti_unfreeze (eti);
1218 }
1219
1220 /*
1221 * Callback routine: invoked when the ETableModel has suffered a change
1222 */
1223
1224 static void
eti_table_model_changed(ETableModel * table_model,ETableItem * eti)1225 eti_table_model_changed (ETableModel *table_model,
1226 ETableItem *eti)
1227 {
1228 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1229
1230 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1231 eti_unfreeze (eti);
1232 return;
1233 }
1234
1235 eti->rows = e_table_model_row_count (eti->table_model);
1236
1237 free_height_cache (eti);
1238
1239 eti_unfreeze (eti);
1240
1241 eti->needs_compute_height = 1;
1242 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1243 eti->needs_redraw = 1;
1244 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1245
1246 eti_idle_maybe_show_cursor (eti);
1247 }
1248
1249 static void
eti_table_model_row_changed(ETableModel * table_model,gint row,ETableItem * eti)1250 eti_table_model_row_changed (ETableModel *table_model,
1251 gint row,
1252 ETableItem *eti)
1253 {
1254 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1255
1256 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1257 eti_unfreeze (eti);
1258 return;
1259 }
1260
1261 if ((!eti->uniform_row_height) && eti->height_cache && eti->height_cache[row] != -1 && eti_row_height_real (eti, row) != eti->height_cache[row]) {
1262 eti_table_model_changed (table_model, eti);
1263 return;
1264 }
1265
1266 eti_unfreeze (eti);
1267
1268 e_table_item_redraw_row (eti, row);
1269 }
1270
1271 static void
eti_table_model_cell_changed(ETableModel * table_model,gint col,gint row,ETableItem * eti)1272 eti_table_model_cell_changed (ETableModel *table_model,
1273 gint col,
1274 gint row,
1275 ETableItem *eti)
1276 {
1277 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1278
1279 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1280 eti_unfreeze (eti);
1281 return;
1282 }
1283
1284 if ((!eti->uniform_row_height) && eti->height_cache && eti->height_cache[row] != -1 && eti_row_height_real (eti, row) != eti->height_cache[row]) {
1285 eti_table_model_changed (table_model, eti);
1286 return;
1287 }
1288
1289 eti_unfreeze (eti);
1290
1291 e_table_item_redraw_row (eti, row);
1292 }
1293
1294 static void
eti_table_model_rows_inserted(ETableModel * table_model,gint row,gint count,ETableItem * eti)1295 eti_table_model_rows_inserted (ETableModel *table_model,
1296 gint row,
1297 gint count,
1298 ETableItem *eti)
1299 {
1300 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1301
1302 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1303 eti_unfreeze (eti);
1304 return;
1305 }
1306 eti->rows = e_table_model_row_count (eti->table_model);
1307
1308 if (eti->height_cache) {
1309 gint i;
1310 eti->height_cache = g_renew (int, eti->height_cache, eti->rows);
1311 memmove (eti->height_cache + row + count, eti->height_cache + row, (eti->rows - count - row) * sizeof (gint));
1312 for (i = row; i < row + count; i++)
1313 eti->height_cache[i] = -1;
1314 }
1315
1316 eti_unfreeze (eti);
1317
1318 eti_idle_maybe_show_cursor (eti);
1319
1320 eti->needs_compute_height = 1;
1321 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1322 eti->needs_redraw = 1;
1323 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1324 }
1325
1326 static void
eti_table_model_rows_deleted(ETableModel * table_model,gint row,gint count,ETableItem * eti)1327 eti_table_model_rows_deleted (ETableModel *table_model,
1328 gint row,
1329 gint count,
1330 ETableItem *eti)
1331 {
1332 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
1333
1334 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED)) {
1335 eti_unfreeze (eti);
1336 return;
1337 }
1338
1339 eti->rows = e_table_model_row_count (eti->table_model);
1340
1341 if (eti->height_cache && (eti->rows > row)) {
1342 memmove (eti->height_cache + row, eti->height_cache + row + count, (eti->rows - row) * sizeof (gint));
1343 }
1344
1345 eti_unfreeze (eti);
1346
1347 eti_idle_maybe_show_cursor (eti);
1348
1349 eti->needs_compute_height = 1;
1350 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1351 eti->needs_redraw = 1;
1352 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1353 }
1354
1355 /**
1356 * e_table_item_redraw_range
1357 * @eti: %ETableItem which will be redrawn
1358 * @start_col: The first col to redraw.
1359 * @start_row: The first row to redraw.
1360 * @end_col: The last col to redraw.
1361 * @end_row: The last row to redraw.
1362 *
1363 * This routine redraws the given %ETableItem in the range given. The
1364 * range is inclusive at both ends.
1365 */
1366 void
e_table_item_redraw_range(ETableItem * eti,gint start_col,gint start_row,gint end_col,gint end_row)1367 e_table_item_redraw_range (ETableItem *eti,
1368 gint start_col,
1369 gint start_row,
1370 gint end_col,
1371 gint end_row)
1372 {
1373 gint border;
1374 gint cursor_col, cursor_row;
1375
1376 g_return_if_fail (eti != NULL);
1377 g_return_if_fail (E_IS_TABLE_ITEM (eti));
1378
1379 g_object_get (
1380 eti->selection,
1381 "cursor_col", &cursor_col,
1382 "cursor_row", &cursor_row,
1383 NULL);
1384
1385 if ((start_col == cursor_col) ||
1386 (end_col == cursor_col) ||
1387 (view_to_model_row (eti, start_row) == cursor_row) ||
1388 (view_to_model_row (eti, end_row) == cursor_row))
1389 border = 2;
1390 else
1391 border = 0;
1392
1393 eti_request_region_redraw (eti, start_col, start_row, end_col, end_row, border);
1394 }
1395
1396 static void
e_table_item_redraw_row(ETableItem * eti,gint row)1397 e_table_item_redraw_row (ETableItem *eti,
1398 gint row)
1399 {
1400 if (row != -1)
1401 e_table_item_redraw_range (eti, 0, row, eti->cols - 1, row);
1402 }
1403
1404 static void
eti_add_table_model(ETableItem * eti,ETableModel * table_model)1405 eti_add_table_model (ETableItem *eti,
1406 ETableModel *table_model)
1407 {
1408 g_return_if_fail (eti->table_model == NULL);
1409
1410 eti->table_model = table_model;
1411 g_object_ref (eti->table_model);
1412
1413 eti->table_model_pre_change_id = g_signal_connect (
1414 table_model, "model_pre_change",
1415 G_CALLBACK (eti_table_model_pre_change), eti);
1416
1417 eti->table_model_no_change_id = g_signal_connect (
1418 table_model, "model_no_change",
1419 G_CALLBACK (eti_table_model_no_change), eti);
1420
1421 eti->table_model_change_id = g_signal_connect (
1422 table_model, "model_changed",
1423 G_CALLBACK (eti_table_model_changed), eti);
1424
1425 eti->table_model_row_change_id = g_signal_connect (
1426 table_model, "model_row_changed",
1427 G_CALLBACK (eti_table_model_row_changed), eti);
1428
1429 eti->table_model_cell_change_id = g_signal_connect (
1430 table_model, "model_cell_changed",
1431 G_CALLBACK (eti_table_model_cell_changed), eti);
1432
1433 eti->table_model_rows_inserted_id = g_signal_connect (
1434 table_model, "model_rows_inserted",
1435 G_CALLBACK (eti_table_model_rows_inserted), eti);
1436
1437 eti->table_model_rows_deleted_id = g_signal_connect (
1438 table_model, "model_rows_deleted",
1439 G_CALLBACK (eti_table_model_rows_deleted), eti);
1440
1441 if (eti->header) {
1442 eti_detach_cell_views (eti);
1443 eti_attach_cell_views (eti);
1444 }
1445
1446 if (E_IS_TABLE_SUBSET (table_model)) {
1447 eti->uses_source_model = 1;
1448 eti->source_model = e_table_subset_get_source_model (
1449 E_TABLE_SUBSET (table_model));
1450 if (eti->source_model)
1451 g_object_ref (eti->source_model);
1452 }
1453
1454 eti_freeze (eti);
1455
1456 eti_table_model_changed (table_model, eti);
1457 }
1458
1459 static void
eti_add_selection_model(ETableItem * eti,ESelectionModel * selection)1460 eti_add_selection_model (ETableItem *eti,
1461 ESelectionModel *selection)
1462 {
1463 g_return_if_fail (eti->selection == NULL);
1464
1465 eti->selection = selection;
1466 g_object_ref (eti->selection);
1467
1468 eti->selection_change_id = g_signal_connect (
1469 selection, "selection_changed",
1470 G_CALLBACK (eti_selection_change), eti);
1471
1472 eti->selection_row_change_id = g_signal_connect (
1473 selection, "selection_row_changed",
1474 G_CALLBACK (eti_selection_row_change), eti);
1475
1476 eti->cursor_change_id = g_signal_connect (
1477 selection, "cursor_changed",
1478 G_CALLBACK (eti_cursor_change), eti);
1479
1480 eti->cursor_activated_id = g_signal_connect (
1481 selection, "cursor_activated",
1482 G_CALLBACK (eti_cursor_activated), eti);
1483
1484 eti_selection_change (selection, eti);
1485 g_signal_emit_by_name (eti, "selection_model_added", eti->selection);
1486 }
1487
1488 static void
eti_header_dim_changed(ETableHeader * eth,gint col,ETableItem * eti)1489 eti_header_dim_changed (ETableHeader *eth,
1490 gint col,
1491 ETableItem *eti)
1492 {
1493 eti->needs_compute_width = 1;
1494 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1495 eti->needs_redraw = 1;
1496 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1497 }
1498
1499 static void
eti_header_structure_changed(ETableHeader * eth,ETableItem * eti)1500 eti_header_structure_changed (ETableHeader *eth,
1501 ETableItem *eti)
1502 {
1503 eti->cols = e_table_header_count (eti->header);
1504
1505 /*
1506 * There should be at least one column
1507 * BUT: then you can't remove all columns from a header and add new ones.
1508 */
1509
1510 if (eti->cell_views) {
1511 eti_unrealize_cell_views (eti);
1512 eti_detach_cell_views (eti);
1513 eti_attach_cell_views (eti);
1514 eti_realize_cell_views (eti);
1515 } else {
1516 if (eti->table_model) {
1517 eti_attach_cell_views (eti);
1518 eti_realize_cell_views (eti);
1519 }
1520 }
1521 eti->needs_compute_width = 1;
1522 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1523 eti->needs_redraw = 1;
1524 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1525 }
1526
1527 static gint
eti_request_column_width(ETableHeader * eth,gint col,ETableItem * eti)1528 eti_request_column_width (ETableHeader *eth,
1529 gint col,
1530 ETableItem *eti)
1531 {
1532 gint width = 0;
1533
1534 if (eti->cell_views && eti->cell_views_realized) {
1535 width = e_cell_max_width (eti->cell_views[col], view_to_model_col (eti, col), col);
1536 }
1537
1538 return width;
1539 }
1540
1541 static void
eti_add_header_model(ETableItem * eti,ETableHeader * header)1542 eti_add_header_model (ETableItem *eti,
1543 ETableHeader *header)
1544 {
1545 g_return_if_fail (eti->header == NULL);
1546
1547 eti->header = header;
1548 g_object_ref (header);
1549
1550 eti_header_structure_changed (header, eti);
1551
1552 eti->header_dim_change_id = g_signal_connect (
1553 header, "dimension_change",
1554 G_CALLBACK (eti_header_dim_changed), eti);
1555
1556 eti->header_structure_change_id = g_signal_connect (
1557 header, "structure_change",
1558 G_CALLBACK (eti_header_structure_changed), eti);
1559
1560 eti->header_request_width_id = g_signal_connect (
1561 header, "request_width",
1562 G_CALLBACK (eti_request_column_width), eti);
1563 }
1564
1565 /*
1566 * GObject::dispose method
1567 */
1568 static void
eti_dispose(GObject * object)1569 eti_dispose (GObject *object)
1570 {
1571 ETableItem *eti = E_TABLE_ITEM (object);
1572 ETableItemPrivate *priv = E_TABLE_ITEM_GET_PRIVATE (eti);
1573
1574 if (priv->show_cursor_delay_source) {
1575 g_source_destroy (priv->show_cursor_delay_source);
1576 g_source_unref (priv->show_cursor_delay_source);
1577 priv->show_cursor_delay_source = NULL;
1578 }
1579
1580 eti_remove_header_model (eti);
1581 eti_remove_table_model (eti);
1582 eti_remove_selection_model (eti);
1583
1584 if (eti->height_cache_idle_id) {
1585 g_source_remove (eti->height_cache_idle_id);
1586 eti->height_cache_idle_id = 0;
1587 }
1588 eti->height_cache_idle_count = 0;
1589
1590 if (eti->cursor_idle_id) {
1591 g_source_remove (eti->cursor_idle_id);
1592 eti->cursor_idle_id = 0;
1593 }
1594
1595 g_clear_pointer (&eti->height_cache, g_free);
1596
1597 /* Chain up to parent's dispose() method. */
1598 G_OBJECT_CLASS (e_table_item_parent_class)->dispose (object);
1599 }
1600
1601 static void
eti_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1602 eti_set_property (GObject *object,
1603 guint property_id,
1604 const GValue *value,
1605 GParamSpec *pspec)
1606 {
1607 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (object);
1608 ETableItem *eti = E_TABLE_ITEM (object);
1609 gint cursor_col;
1610
1611 switch (property_id) {
1612 case PROP_TABLE_HEADER:
1613 eti_remove_header_model (eti);
1614 eti_add_header_model (eti, E_TABLE_HEADER (g_value_get_object (value)));
1615 break;
1616
1617 case PROP_TABLE_MODEL:
1618 eti_remove_table_model (eti);
1619 eti_add_table_model (eti, E_TABLE_MODEL (g_value_get_object (value)));
1620 break;
1621
1622 case PROP_SELECTION_MODEL:
1623 g_signal_emit_by_name (
1624 eti, "selection_model_removed", eti->selection);
1625 eti_remove_selection_model (eti);
1626 if (g_value_get_object (value))
1627 eti_add_selection_model (eti, E_SELECTION_MODEL (g_value_get_object (value)));
1628 break;
1629
1630 case PROP_LENGTH_THRESHOLD:
1631 eti->length_threshold = g_value_get_int (value);
1632 break;
1633
1634 case PROP_TABLE_ALTERNATING_ROW_COLORS:
1635 eti->alternating_row_colors = g_value_get_boolean (value);
1636 break;
1637
1638 case PROP_TABLE_HORIZONTAL_DRAW_GRID:
1639 eti->horizontal_draw_grid = g_value_get_boolean (value);
1640 break;
1641
1642 case PROP_TABLE_VERTICAL_DRAW_GRID:
1643 eti->vertical_draw_grid = g_value_get_boolean (value);
1644 break;
1645
1646 case PROP_TABLE_DRAW_FOCUS:
1647 eti->draw_focus = g_value_get_boolean (value);
1648 break;
1649
1650 case PROP_CURSOR_MODE:
1651 eti->cursor_mode = g_value_get_int (value);
1652 break;
1653
1654 case PROP_MINIMUM_WIDTH:
1655 case PROP_WIDTH:
1656 if ((eti->minimum_width == eti->width && g_value_get_double (value) > eti->width) ||
1657 g_value_get_double (value) < eti->width) {
1658 eti->needs_compute_width = 1;
1659 e_canvas_item_request_reflow (item);
1660 }
1661 eti->minimum_width = g_value_get_double (value);
1662 break;
1663 case PROP_CURSOR_ROW:
1664 g_object_get (
1665 eti->selection,
1666 "cursor_col", &cursor_col,
1667 NULL);
1668
1669 e_table_item_focus (eti, cursor_col != -1 ? cursor_col : 0, view_to_model_row (eti, g_value_get_int (value)), 0);
1670 break;
1671 case PROP_UNIFORM_ROW_HEIGHT:
1672 if (eti->uniform_row_height != g_value_get_boolean (value)) {
1673 eti->uniform_row_height = g_value_get_boolean (value);
1674 if (item->flags & GNOME_CANVAS_ITEM_REALIZED) {
1675 free_height_cache (eti);
1676 eti->needs_compute_height = 1;
1677 e_canvas_item_request_reflow (item);
1678 eti->needs_redraw = 1;
1679 gnome_canvas_item_request_update (item);
1680 }
1681 }
1682 break;
1683 }
1684 eti->needs_redraw = 1;
1685 gnome_canvas_item_request_update (item);
1686 }
1687
1688 static void
eti_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1689 eti_get_property (GObject *object,
1690 guint property_id,
1691 GValue *value,
1692 GParamSpec *pspec)
1693 {
1694 ETableItem *eti;
1695 gint row;
1696
1697 eti = E_TABLE_ITEM (object);
1698
1699 switch (property_id) {
1700 case PROP_WIDTH:
1701 g_value_set_double (value, eti->width);
1702 break;
1703 case PROP_HEIGHT:
1704 g_value_set_double (value, eti->height);
1705 break;
1706 case PROP_MINIMUM_WIDTH:
1707 g_value_set_double (value, eti->minimum_width);
1708 break;
1709 case PROP_CURSOR_ROW:
1710 g_object_get (
1711 eti->selection,
1712 "cursor_row", &row,
1713 NULL);
1714 g_value_set_int (value, model_to_view_row (eti, row));
1715 break;
1716 case PROP_UNIFORM_ROW_HEIGHT:
1717 g_value_set_boolean (value, eti->uniform_row_height);
1718 break;
1719 case PROP_IS_EDITING:
1720 g_value_set_boolean (value, e_table_item_is_editing (eti));
1721 break;
1722 default:
1723 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1724 break;
1725 }
1726 }
1727
1728 static void
e_table_item_init(ETableItem * eti)1729 e_table_item_init (ETableItem *eti)
1730 {
1731 /* eti->priv = E_TABLE_ITEM_GET_PRIVATE (eti); */
1732
1733 eti->motion_row = -1;
1734 eti->motion_col = -1;
1735 eti->editing_col = -1;
1736 eti->editing_row = -1;
1737 eti->height = 0;
1738 eti->width = 0;
1739 eti->minimum_width = 0;
1740
1741 eti->save_col = -1;
1742 eti->save_row = -1;
1743 eti->save_state = NULL;
1744
1745 eti->click_count = 0;
1746
1747 eti->height_cache = NULL;
1748 eti->height_cache_idle_id = 0;
1749 eti->height_cache_idle_count = 0;
1750
1751 eti->length_threshold = -1;
1752 eti->uniform_row_height = FALSE;
1753
1754 eti->uses_source_model = 0;
1755 eti->source_model = NULL;
1756
1757 eti->row_guess = -1;
1758 eti->cursor_mode = E_CURSOR_SIMPLE;
1759
1760 eti->selection_change_id = 0;
1761 eti->selection_row_change_id = 0;
1762 eti->cursor_change_id = 0;
1763 eti->cursor_activated_id = 0;
1764 eti->selection = NULL;
1765
1766 eti->old_cursor_row = -1;
1767
1768 eti->needs_redraw = 0;
1769 eti->needs_compute_height = 0;
1770
1771 eti->in_key_press = 0;
1772
1773 eti->maybe_did_something = TRUE;
1774
1775 eti->grabbed_count = 0;
1776 eti->gtk_grabbed = 0;
1777
1778 eti->in_drag = 0;
1779 eti->maybe_in_drag = 0;
1780
1781 eti->grabbed_col = -1;
1782 eti->grabbed_row = -1;
1783
1784 eti->cursor_on_screen = FALSE;
1785 eti->cursor_x1 = -1;
1786 eti->cursor_y1 = -1;
1787 eti->cursor_x2 = -1;
1788 eti->cursor_y2 = -1;
1789
1790 eti->rows = -1;
1791 eti->cols = -1;
1792
1793 eti->frozen_count = 0;
1794 eti->queue_show_cursor = FALSE;
1795
1796 e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (eti), eti_reflow);
1797 }
1798
1799 static gboolean
eti_tree_unfreeze(GtkWidget * widget,GdkEvent * event,ETableItem * eti)1800 eti_tree_unfreeze (GtkWidget *widget,
1801 GdkEvent *event,
1802 ETableItem *eti)
1803 {
1804
1805 if (widget)
1806 g_object_set_data (G_OBJECT (widget), "freeze-cursor", NULL);
1807
1808 return FALSE;
1809 }
1810
1811 static void
eti_realize(GnomeCanvasItem * item)1812 eti_realize (GnomeCanvasItem *item)
1813 {
1814 ETableItem *eti = E_TABLE_ITEM (item);
1815
1816 if (GNOME_CANVAS_ITEM_CLASS (e_table_item_parent_class)->realize)
1817 (*GNOME_CANVAS_ITEM_CLASS (e_table_item_parent_class)->realize)(item);
1818
1819 eti->rows = e_table_model_row_count (eti->table_model);
1820
1821 g_signal_connect (
1822 item->canvas, "scroll_event",
1823 G_CALLBACK (eti_tree_unfreeze), eti);
1824
1825 if (eti->cell_views == NULL)
1826 eti_attach_cell_views (eti);
1827
1828 eti_realize_cell_views (eti);
1829
1830 free_height_cache (eti);
1831
1832 if (item->canvas->focused_item == NULL && eti->selection) {
1833 gint row;
1834
1835 row = e_selection_model_cursor_row (E_SELECTION_MODEL (eti->selection));
1836 row = model_to_view_row (eti, row);
1837 if (row != -1) {
1838 e_canvas_item_grab_focus (item, FALSE);
1839 eti_show_cursor (eti, 0);
1840 eti_check_cursor_bounds (eti);
1841 }
1842 }
1843
1844 eti->needs_compute_height = 1;
1845 eti->needs_compute_width = 1;
1846 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
1847 eti->needs_redraw = 1;
1848 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
1849 }
1850
1851 static void
eti_unrealize(GnomeCanvasItem * item)1852 eti_unrealize (GnomeCanvasItem *item)
1853 {
1854 ETableItem *eti = E_TABLE_ITEM (item);
1855
1856 if (eti->grabbed_count > 0) {
1857 d (g_print ("%s: eti_ungrab\n", G_STRFUNC));
1858 eti_ungrab (eti, -1);
1859 }
1860
1861 if (eti_editing (eti))
1862 e_table_item_leave_edit_(eti);
1863
1864 if (eti->height_cache_idle_id) {
1865 g_source_remove (eti->height_cache_idle_id);
1866 eti->height_cache_idle_id = 0;
1867 }
1868
1869 g_clear_pointer (&eti->height_cache, g_free);
1870 eti->height_cache_idle_count = 0;
1871
1872 eti_unrealize_cell_views (eti);
1873
1874 eti->height = 0;
1875
1876 if (GNOME_CANVAS_ITEM_CLASS (e_table_item_parent_class)->unrealize)
1877 (*GNOME_CANVAS_ITEM_CLASS (e_table_item_parent_class)->unrealize)(item);
1878 }
1879
1880 gboolean
e_table_item_get_row_selected(ETableItem * eti,gint row)1881 e_table_item_get_row_selected (ETableItem *eti,
1882 gint row)
1883 {
1884 g_return_val_if_fail (E_IS_TABLE_ITEM (eti), FALSE);
1885
1886 return row >= 0 && row < eti->rows &&
1887 e_selection_model_is_row_selected (E_SELECTION_MODEL (eti->selection), view_to_model_row (eti, row));
1888 }
1889
1890 static void
eti_draw_grid_line(ETableItem * eti,cairo_t * cr,const GdkRGBA * rgba,gint x1,gint y1,gint x2,gint y2)1891 eti_draw_grid_line (ETableItem *eti,
1892 cairo_t *cr,
1893 const GdkRGBA *rgba,
1894 gint x1,
1895 gint y1,
1896 gint x2,
1897 gint y2)
1898 {
1899 cairo_save (cr);
1900
1901 cairo_set_line_width (cr, 0.5);
1902 gdk_cairo_set_source_rgba (cr, rgba);
1903
1904 cairo_move_to (cr, x1 - 0.5, y1 + 0.5);
1905 cairo_line_to (cr, x2 - 0.5, y2 + 0.5);
1906 cairo_stroke (cr);
1907
1908 cairo_restore (cr);
1909 }
1910
1911 static void
eti_draw(GnomeCanvasItem * item,cairo_t * cr,gint x,gint y,gint width,gint height)1912 eti_draw (GnomeCanvasItem *item,
1913 cairo_t *cr,
1914 gint x,
1915 gint y,
1916 gint width,
1917 gint height)
1918 {
1919 ETableItem *eti = E_TABLE_ITEM (item);
1920 const gint rows = eti->rows;
1921 const gint cols = eti->cols;
1922 gint row, col;
1923 gint first_col, last_col, x_offset;
1924 gint first_row, last_row, y_offset, yd;
1925 gint x1, x2;
1926 gint f_x1, f_x2, f_y1, f_y2;
1927 gboolean f_found;
1928 cairo_matrix_t i2c;
1929 gdouble eti_base_x, eti_base_y, lower_right_y, lower_right_x;
1930 GtkWidget *canvas = GTK_WIDGET (item->canvas);
1931 GdkRGBA line_color;
1932 gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
1933
1934 e_utils_get_theme_color (canvas, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &line_color);
1935 e_utils_shade_color (&line_color, &line_color, E_UTILS_DARKNESS_MULT);
1936
1937 /*
1938 * Find out our real position after grouping
1939 */
1940 gnome_canvas_item_i2c_matrix (item, &i2c);
1941 eti_base_x = 0;
1942 eti_base_y = 0;
1943 cairo_matrix_transform_point (&i2c, &eti_base_x, &eti_base_y);
1944
1945 lower_right_x = eti->width;
1946 lower_right_y = eti->height;
1947 cairo_matrix_transform_point (&i2c, &lower_right_x, &lower_right_y);
1948
1949 /*
1950 * First column to draw, last column to draw
1951 */
1952 first_col = -1;
1953 x_offset = 0;
1954 x1 = floor (eti_base_x);
1955 for (col = 0; col < cols; col++, x1 = x2) {
1956 ETableCol *ecol = e_table_header_get_column (eti->header, col);
1957
1958 x2 = x1 + ecol->width;
1959
1960 if (x1 > (x + width))
1961 break;
1962 if (x2 < x)
1963 continue;
1964 if (first_col == -1) {
1965 x_offset = x1 - x;
1966 first_col = col;
1967 }
1968 }
1969 last_col = col;
1970
1971 /*
1972 * Nothing to paint
1973 */
1974 if (first_col == -1)
1975 return;
1976
1977 /*
1978 * Compute row span.
1979 */
1980 if (eti->uniform_row_height) {
1981 first_row = (y - floor (eti_base_y) - height_extra) / (ETI_ROW_HEIGHT (eti, -1) + height_extra);
1982 last_row = (y + height - floor (eti_base_y) ) / (ETI_ROW_HEIGHT (eti, -1) + height_extra) + 1;
1983 if (first_row > last_row)
1984 return;
1985 y_offset = floor (eti_base_y) - y + height_extra + first_row * (ETI_ROW_HEIGHT (eti, -1) + height_extra);
1986 if (first_row < 0)
1987 first_row = 0;
1988 if (last_row > eti->rows)
1989 last_row = eti->rows;
1990 } else {
1991 gint y1, y2;
1992
1993 y_offset = 0;
1994 first_row = -1;
1995
1996 y1 = y2 = floor (eti_base_y) + height_extra;
1997 for (row = 0; row < rows; row++, y1 = y2) {
1998
1999 y2 += ETI_ROW_HEIGHT (eti, row) + height_extra;
2000
2001 if (y1 > y + height)
2002 break;
2003
2004 if (y2 < y)
2005 continue;
2006
2007 if (first_row == -1) {
2008 y_offset = y1 - y;
2009 first_row = row;
2010 }
2011 }
2012 last_row = row;
2013
2014 if (first_row == -1)
2015 return;
2016 }
2017
2018 if (first_row == -1)
2019 return;
2020
2021 /*
2022 * Draw cells
2023 */
2024 yd = y_offset;
2025 f_x1 = f_x2 = f_y1 = f_y2 = -1;
2026 f_found = FALSE;
2027
2028 if (eti->horizontal_draw_grid && first_row == 0)
2029 eti_draw_grid_line (eti, cr, &line_color, eti_base_x - x, yd, eti_base_x + eti->width - x, yd);
2030
2031 yd += height_extra;
2032
2033 for (row = first_row; row < last_row; row++) {
2034 gint xd;
2035 gboolean selected;
2036 gint cursor_col, cursor_row;
2037
2038 height = ETI_ROW_HEIGHT (eti, row);
2039
2040 xd = x_offset;
2041
2042 selected = e_table_item_get_row_selected (eti, row);
2043
2044 g_object_get (
2045 eti->selection,
2046 "cursor_col", &cursor_col,
2047 "cursor_row", &cursor_row,
2048 NULL);
2049
2050 for (col = first_col; col < last_col; col++) {
2051 ETableCol *ecol = e_table_header_get_column (eti->header, col);
2052 ECellView *ecell_view = eti->cell_views[col];
2053 gboolean col_selected = selected;
2054 gboolean cursor = FALSE;
2055 ECellFlags flags;
2056 GdkRGBA background;
2057 gint x1, x2, y1, y2;
2058 cairo_pattern_t *pat;
2059
2060 switch (eti->cursor_mode) {
2061 case E_CURSOR_SIMPLE:
2062 case E_CURSOR_SPREADSHEET:
2063 if (cursor_col == ecol->spec->model_col && cursor_row == view_to_model_row (eti, row)) {
2064 col_selected = !col_selected;
2065 cursor = TRUE;
2066 }
2067 break;
2068 case E_CURSOR_LINE:
2069 /* Nothing */
2070 break;
2071 }
2072
2073 x1 = xd;
2074 y1 = yd + 1;
2075 x2 = x1 + ecol->width;
2076 y2 = yd + height;
2077
2078 eti_get_cell_background_color (eti, row, col, col_selected, &background);
2079
2080 cairo_save (cr);
2081 pat = cairo_pattern_create_linear (0, y1, 0, y2);
2082 cairo_pattern_add_color_stop_rgba (
2083 pat, 0.0, background.red,
2084 background.green,
2085 background.blue, selected ? 0.8: 1.0);
2086 if (selected)
2087 cairo_pattern_add_color_stop_rgba (
2088 pat, 0.5, background.red,
2089 background.green,
2090 background.blue, 0.9);
2091
2092 cairo_pattern_add_color_stop_rgba (
2093 pat, 1, background.red,
2094 background.green,
2095 background.blue, selected ? 0.8 : 1.0);
2096 cairo_rectangle (cr, x1, y1, ecol->width, height - 1);
2097 cairo_set_source (cr, pat);
2098 cairo_fill_preserve (cr);
2099 cairo_pattern_destroy (pat);
2100 cairo_set_line_width (cr, 0);
2101 cairo_stroke (cr);
2102 cairo_restore (cr);
2103
2104 cairo_save (cr);
2105 cairo_set_line_width (cr, 0.5);
2106 cairo_set_source_rgba (
2107 cr, background.red,
2108 background.green,
2109 background.blue, 1);
2110 cairo_move_to (cr, x1, y1 + 0.5);
2111 cairo_line_to (cr, x2, y1 + 0.5);
2112 cairo_stroke (cr);
2113
2114 cairo_set_line_width (cr, 0.5);
2115 cairo_set_source_rgba (
2116 cr, background.red,
2117 background.green,
2118 background.blue, 1);
2119 cairo_move_to (cr, x1, y2 + 0.5);
2120 cairo_line_to (cr, x2, y2 + 0.5);
2121 cairo_stroke (cr);
2122 cairo_restore (cr);
2123
2124 flags = col_selected ? E_CELL_SELECTED : 0;
2125 flags |= gtk_widget_has_focus (canvas) ? E_CELL_FOCUSED : 0;
2126 flags |= cursor ? E_CELL_CURSOR : 0;
2127
2128 switch (ecol->justification) {
2129 case GTK_JUSTIFY_LEFT:
2130 flags |= E_CELL_JUSTIFY_LEFT;
2131 break;
2132 case GTK_JUSTIFY_RIGHT:
2133 flags |= E_CELL_JUSTIFY_RIGHT;
2134 break;
2135 case GTK_JUSTIFY_CENTER:
2136 flags |= E_CELL_JUSTIFY_CENTER;
2137 break;
2138 case GTK_JUSTIFY_FILL:
2139 flags |= E_CELL_JUSTIFY_FILL;
2140 break;
2141 }
2142
2143 e_cell_draw (
2144 ecell_view, cr,
2145 ecol->spec->model_col,
2146 col, row, flags,
2147 xd, yd,
2148 xd + ecol->width, yd + height);
2149
2150 if (!f_found && !selected) {
2151 switch (eti->cursor_mode) {
2152 case E_CURSOR_LINE:
2153 if (view_to_model_row (eti, row) == cursor_row) {
2154 f_x1 = floor (eti_base_x) - x;
2155 f_x2 = floor (lower_right_x) - x;
2156 f_y1 = yd + 1;
2157 f_y2 = yd + height;
2158 f_found = TRUE;
2159 }
2160 break;
2161 case E_CURSOR_SIMPLE:
2162 case E_CURSOR_SPREADSHEET:
2163 if (view_to_model_col (eti, col) == cursor_col && view_to_model_row (eti, row) == cursor_row) {
2164 f_x1 = xd;
2165 f_x2 = xd + ecol->width;
2166 f_y1 = yd;
2167 f_y2 = yd + height;
2168 f_found = TRUE;
2169 }
2170 break;
2171 }
2172 }
2173
2174 xd += ecol->width;
2175 }
2176 yd += height;
2177
2178 if (eti->horizontal_draw_grid) {
2179 eti_draw_grid_line (eti, cr, &line_color, eti_base_x - x, yd, eti_base_x + eti->width - x, yd);
2180 yd++;
2181 }
2182 }
2183
2184 if (eti->vertical_draw_grid) {
2185 gint xd = x_offset;
2186
2187 for (col = first_col; col <= last_col; col++) {
2188 ETableCol *ecol = e_table_header_get_column (eti->header, col);
2189
2190 eti_draw_grid_line (eti, cr, &line_color, xd, y_offset, xd, yd - 1);
2191
2192 /*
2193 * This looks wierd, but it is to draw the last line
2194 */
2195 if (ecol)
2196 xd += ecol->width;
2197 }
2198 }
2199
2200 /*
2201 * Draw focus
2202 */
2203 if (eti->draw_focus && f_found) {
2204 static const double dash[] = { 1.0, 1.0 };
2205 GdkRGBA bg, fg;
2206
2207 e_utils_get_theme_color (canvas, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &bg);
2208 e_utils_get_theme_color (canvas, "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR, &fg);
2209
2210 cairo_set_line_width (cr, 1.0);
2211 cairo_rectangle (
2212 cr,
2213 f_x1 + 0.5, f_x2 + 0.5,
2214 f_x2 - f_x1 - 1, f_y2 - f_y1);
2215
2216 gdk_cairo_set_source_rgba (cr, &bg);
2217 cairo_stroke_preserve (cr);
2218
2219 cairo_set_dash (cr, dash, G_N_ELEMENTS (dash), 0.0);
2220 gdk_cairo_set_source_rgba (cr, &fg);
2221 cairo_stroke (cr);
2222 }
2223 }
2224
2225 static GnomeCanvasItem *
eti_point(GnomeCanvasItem * item,gdouble x,gdouble y,gint cx,gint cy)2226 eti_point (GnomeCanvasItem *item,
2227 gdouble x,
2228 gdouble y,
2229 gint cx,
2230 gint cy)
2231 {
2232 return item;
2233 }
2234
2235 static gboolean
find_cell(ETableItem * eti,gdouble x,gdouble y,gint * view_col_res,gint * view_row_res,gdouble * x1_res,gdouble * y1_res)2236 find_cell (ETableItem *eti,
2237 gdouble x,
2238 gdouble y,
2239 gint *view_col_res,
2240 gint *view_row_res,
2241 gdouble *x1_res,
2242 gdouble *y1_res)
2243 {
2244 const gint cols = eti->cols;
2245 const gint rows = eti->rows;
2246 gdouble x1, y1, x2, y2;
2247 gint col, row;
2248
2249 gint height_extra = eti->horizontal_draw_grid ? 1 : 0;
2250
2251 /* FIXME: this routine is inneficient, fix later */
2252
2253 if (eti->grabbed_col >= 0 && eti->grabbed_row >= 0) {
2254 *view_col_res = eti->grabbed_col;
2255 *view_row_res = eti->grabbed_row;
2256 *x1_res = x - e_table_header_col_diff (eti->header, 0, eti->grabbed_col);
2257 *y1_res = y - e_table_item_row_diff (eti, 0, eti->grabbed_row);
2258 return TRUE;
2259 }
2260
2261 if (cols == 0 || rows == 0)
2262 return FALSE;
2263
2264 x1 = 0;
2265 for (col = 0; col < cols - 1; col++, x1 = x2) {
2266 ETableCol *ecol = e_table_header_get_column (eti->header, col);
2267
2268 if (x < x1)
2269 return FALSE;
2270
2271 x2 = x1 + ecol->width;
2272
2273 if (x <= x2)
2274 break;
2275 }
2276
2277 if (eti->uniform_row_height) {
2278 if (y < height_extra)
2279 return FALSE;
2280 row = (y - height_extra) / (ETI_ROW_HEIGHT (eti, -1) + height_extra);
2281 y1 = row * (ETI_ROW_HEIGHT (eti, -1) + height_extra) + height_extra;
2282 if (row >= eti->rows)
2283 return FALSE;
2284 } else {
2285 y1 = y2 = height_extra;
2286 if (y < height_extra)
2287 return FALSE;
2288 for (row = 0; row < rows; row++, y1 = y2) {
2289 y2 += ETI_ROW_HEIGHT (eti, row) + height_extra;
2290
2291 if (y <= y2)
2292 break;
2293 }
2294
2295 if (row == rows)
2296 return FALSE;
2297 }
2298 *view_col_res = col;
2299 if (x1_res)
2300 *x1_res = x - x1;
2301 *view_row_res = row;
2302 if (y1_res)
2303 *y1_res = y - y1;
2304 return TRUE;
2305 }
2306
2307 static void
eti_cursor_move(ETableItem * eti,gint row,gint column)2308 eti_cursor_move (ETableItem *eti,
2309 gint row,
2310 gint column)
2311 {
2312 e_table_item_leave_edit_(eti);
2313 e_table_item_focus (eti, view_to_model_col (eti, column), view_to_model_row (eti, row), 0);
2314 }
2315
2316 static void
eti_cursor_move_left(ETableItem * eti)2317 eti_cursor_move_left (ETableItem *eti)
2318 {
2319 gint cursor_col, cursor_row;
2320 g_object_get (
2321 eti->selection,
2322 "cursor_col", &cursor_col,
2323 "cursor_row", &cursor_row,
2324 NULL);
2325
2326 eti_cursor_move (eti, model_to_view_row (eti, cursor_row), model_to_view_col (eti, cursor_col) - 1);
2327 }
2328
2329 static void
eti_cursor_move_right(ETableItem * eti)2330 eti_cursor_move_right (ETableItem *eti)
2331 {
2332 gint cursor_col, cursor_row;
2333 g_object_get (
2334 eti->selection,
2335 "cursor_col", &cursor_col,
2336 "cursor_row", &cursor_row,
2337 NULL);
2338
2339 eti_cursor_move (eti, model_to_view_row (eti, cursor_row), model_to_view_col (eti, cursor_col) + 1);
2340 }
2341
2342 static gint
eti_e_cell_event(ETableItem * item,ECellView * ecell_view,GdkEvent * event,gint model_col,gint view_col,gint row,ECellFlags flags)2343 eti_e_cell_event (ETableItem *item,
2344 ECellView *ecell_view,
2345 GdkEvent *event,
2346 gint model_col,
2347 gint view_col,
2348 gint row,
2349 ECellFlags flags)
2350 {
2351 ECellActions actions = 0;
2352 gint ret_val;
2353
2354 ret_val = e_cell_event (
2355 ecell_view, event, model_col, view_col, row, flags, &actions);
2356
2357 if (actions & E_CELL_GRAB) {
2358 GdkDevice *event_device;
2359 guint32 event_time;
2360
2361 d (g_print ("%s: eti_grab\n", G_STRFUNC));
2362
2363 event_device = gdk_event_get_device (event);
2364 event_time = gdk_event_get_time (event);
2365 eti_grab (item, event_device, event_time);
2366
2367 item->grabbed_col = view_col;
2368 item->grabbed_row = row;
2369 }
2370
2371 if (actions & E_CELL_UNGRAB) {
2372 guint32 event_time;
2373
2374 d (g_print ("%s: eti_ungrab\n", G_STRFUNC));
2375
2376 event_time = gdk_event_get_time (event);
2377 eti_ungrab (item, event_time);
2378
2379 item->grabbed_col = -1;
2380 item->grabbed_row = -1;
2381 }
2382
2383 return ret_val;
2384 }
2385
2386 /* FIXME: cursor */
2387 static gint
eti_event(GnomeCanvasItem * item,GdkEvent * event)2388 eti_event (GnomeCanvasItem *item,
2389 GdkEvent *event)
2390 {
2391 ETableItem *eti = E_TABLE_ITEM (item);
2392 ECellView *ecell_view;
2393 GdkModifierType event_state = 0;
2394 GdkEvent *event_copy;
2395 guint event_button = 0;
2396 guint event_keyval = 0;
2397 gdouble event_x_item = 0;
2398 gdouble event_y_item = 0;
2399 gdouble event_x_win = 0;
2400 gdouble event_y_win = 0;
2401 guint32 event_time;
2402 gboolean return_val = TRUE;
2403 #if d(!)0
2404 gboolean leave = FALSE;
2405 #endif
2406
2407 if (!eti->header)
2408 return FALSE;
2409
2410 /* Don't fetch the device here. GnomeCanvas frequently emits
2411 * synthesized events, and calling gdk_event_get_device() on them
2412 * will trigger a runtime warning. Fetch the device where needed. */
2413 gdk_event_get_button (event, &event_button);
2414 gdk_event_get_coords (event, &event_x_win, &event_y_win);
2415 gdk_event_get_keyval (event, &event_keyval);
2416 gdk_event_get_state (event, &event_state);
2417 event_time = gdk_event_get_time (event);
2418
2419 switch (event->type) {
2420 case GDK_BUTTON_PRESS: {
2421 gdouble x1, y1;
2422 gint col, row;
2423 gint cursor_row, cursor_col;
2424 gint new_cursor_row, new_cursor_col;
2425 ECellFlags flags = 0;
2426
2427 d (g_print ("%s: GDK_BUTTON_PRESS received, button %d\n", G_STRFUNC, event_button));
2428
2429 switch (event_button) {
2430 case 1: /* Fall through. */
2431 case 2:
2432 e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (eti), TRUE);
2433
2434 event_x_item = event_x_win;
2435 event_y_item = event_y_win;
2436
2437 gnome_canvas_item_w2i (
2438 item, &event_x_item, &event_y_item);
2439
2440 if (!find_cell (eti, event_x_item, event_y_item, &col, &row, &x1, &y1)) {
2441 if (eti_editing (eti))
2442 e_table_item_leave_edit_(eti);
2443 return TRUE;
2444 }
2445
2446 ecell_view = eti->cell_views[col];
2447
2448 /* Clone the event and alter its position. */
2449 event_copy = gdk_event_copy (event);
2450 event_copy->button.x = x1;
2451 event_copy->button.y = y1;
2452
2453 g_object_get (
2454 eti->selection,
2455 "cursor_row", &cursor_row,
2456 "cursor_col", &cursor_col,
2457 NULL);
2458
2459 if (cursor_col == view_to_model_col (eti, col) && cursor_row == view_to_model_row (eti, row)) {
2460 flags = E_CELL_CURSOR;
2461 } else {
2462 flags = 0;
2463 }
2464
2465 return_val = eti_e_cell_event (
2466 eti, ecell_view, event_copy,
2467 view_to_model_col (eti, col),
2468 col, row, flags);
2469 if (return_val) {
2470 gdk_event_free (event_copy);
2471 return TRUE;
2472 }
2473
2474 g_signal_emit (
2475 eti, eti_signals[CLICK], 0,
2476 row, view_to_model_col (eti, col),
2477 event_copy, &return_val);
2478
2479 gdk_event_free (event_copy);
2480
2481 if (return_val) {
2482 eti->click_count = 0;
2483 return TRUE;
2484 }
2485
2486 g_object_get (
2487 eti->selection,
2488 "cursor_row", &cursor_row,
2489 "cursor_col", &cursor_col,
2490 NULL);
2491
2492 eti->maybe_did_something =
2493 e_selection_model_maybe_do_something (
2494 E_SELECTION_MODEL (eti->selection),
2495 view_to_model_row (eti, row),
2496 view_to_model_col (eti, col),
2497 event_state);
2498 g_object_get (
2499 eti->selection,
2500 "cursor_row", &new_cursor_row,
2501 "cursor_col", &new_cursor_col,
2502 NULL);
2503
2504 if (cursor_row != new_cursor_row || cursor_col != new_cursor_col) {
2505 eti->click_count = 1;
2506 } else {
2507 eti->click_count++;
2508 eti->row_guess = row;
2509
2510 if ((!eti_editing (eti)) && e_table_model_is_cell_editable (eti->table_model, cursor_col, row)) {
2511 e_table_item_enter_edit (eti, col, row);
2512 }
2513
2514 /*
2515 * Adjust the event positions
2516 */
2517
2518 if (eti_editing (eti)) {
2519 return_val = eti_e_cell_event (
2520 eti, ecell_view, event,
2521 view_to_model_col (eti, col),
2522 col, row,
2523 E_CELL_EDITING |
2524 E_CELL_CURSOR);
2525 if (return_val)
2526 return TRUE;
2527 }
2528 }
2529
2530 if (event_button == 1) {
2531 return_val = TRUE;
2532
2533 eti->maybe_in_drag = TRUE;
2534 eti->drag_row = new_cursor_row;
2535 eti->drag_col = new_cursor_col;
2536 eti->drag_x = event_x_item;
2537 eti->drag_y = event_y_item;
2538 eti->drag_state = event_state;
2539 }
2540
2541 break;
2542 case 3:
2543 e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (eti), TRUE);
2544
2545 event_x_item = event_x_win;
2546 event_y_item = event_y_win;
2547
2548 gnome_canvas_item_w2i (
2549 item, &event_x_item, &event_y_item);
2550
2551 if (!find_cell (eti, event_x_item, event_y_item, &col, &row, &x1, &y1))
2552 return TRUE;
2553
2554 e_selection_model_right_click_down (
2555 E_SELECTION_MODEL (eti->selection),
2556 view_to_model_row (eti, row),
2557 view_to_model_col (eti, col), 0);
2558
2559 /* Clone the event and alter its position. */
2560 event_copy = gdk_event_copy (event);
2561 event_copy->button.x = event_x_item;
2562 event_copy->button.y = event_y_item;
2563
2564 g_signal_emit (
2565 eti, eti_signals[RIGHT_CLICK], 0,
2566 row, view_to_model_col (eti, col),
2567 event, &return_val);
2568
2569 gdk_event_free (event_copy);
2570
2571 if (!return_val)
2572 e_selection_model_right_click_up (E_SELECTION_MODEL (eti->selection));
2573 break;
2574 case 4:
2575 case 5:
2576 return FALSE;
2577
2578 }
2579 break;
2580 }
2581
2582 case GDK_BUTTON_RELEASE: {
2583 gdouble x1, y1;
2584 gint col, row;
2585 gint cursor_row, cursor_col;
2586
2587 d (g_print ("%s: GDK_BUTTON_RELEASE received, button %d\n", G_STRFUNC, event_button));
2588
2589 if (eti->grabbed_count > 0) {
2590 d (g_print ("%s: eti_ungrab\n", G_STRFUNC));
2591 eti_ungrab (eti, event_time);
2592 }
2593
2594 if (event_button == 1) {
2595 if (eti->maybe_in_drag) {
2596 eti->maybe_in_drag = FALSE;
2597 if (!eti->maybe_did_something)
2598 e_selection_model_do_something (E_SELECTION_MODEL (eti->selection), eti->drag_row, eti->drag_col, eti->drag_state);
2599 }
2600 if (eti->in_drag) {
2601 eti->in_drag = FALSE;
2602 }
2603 }
2604
2605 switch (event_button) {
2606 case 1: /* Fall through. */
2607 case 2:
2608
2609 event_x_item = event_x_win;
2610 event_y_item = event_y_win;
2611
2612 gnome_canvas_item_w2i (
2613 item, &event_x_item, &event_y_item);
2614 #if d(!)0
2615 {
2616 gboolean cell_found = find_cell (
2617 eti, event_x_item, event_y_item,
2618 &col, &row, &x1, &y1);
2619 g_print (
2620 "%s: find_cell(%f, %f) = %s(%d, %d, %f, %f)\n",
2621 G_STRFUNC, event_x_item, event_y_item,
2622 cell_found?"true":"false", col, row, x1, y1);
2623 }
2624 #endif
2625
2626 if (!find_cell (eti, event_x_item, event_y_item, &col, &row, &x1, &y1))
2627 return TRUE;
2628
2629 g_object_get (
2630 eti->selection,
2631 "cursor_row", &cursor_row,
2632 "cursor_col", &cursor_col,
2633 NULL);
2634
2635 if (eti_editing (eti) && cursor_row == view_to_model_row (eti, row) && cursor_col == view_to_model_col (eti, col)) {
2636
2637 d (g_print ("%s: GDK_BUTTON_RELEASE received, button %d, line: %d\n", G_STRFUNC, event_button, __LINE__))
2638 ;
2639
2640 ecell_view = eti->cell_views[col];
2641
2642 /* Clone the event and alter its position. */
2643 event_copy = gdk_event_copy (event);
2644 event_copy->button.x = x1;
2645 event_copy->button.y = y1;
2646
2647 return_val = eti_e_cell_event (
2648 eti, ecell_view, event_copy,
2649 view_to_model_col (eti, col),
2650 col, row,
2651 E_CELL_EDITING |
2652 E_CELL_CURSOR);
2653
2654 gdk_event_free (event_copy);
2655 }
2656 break;
2657 case 3:
2658 e_selection_model_right_click_up (E_SELECTION_MODEL (eti->selection));
2659 return_val = TRUE;
2660 break;
2661 case 4:
2662 case 5:
2663 return FALSE;
2664
2665 }
2666 break;
2667 }
2668
2669 case GDK_2BUTTON_PRESS: {
2670 gint model_col, model_row;
2671 #if 0
2672 gdouble x1, y1;
2673 #endif
2674
2675 d (g_print ("%s: GDK_2BUTTON_PRESS received, button %d\n", G_STRFUNC, event_button));
2676
2677 /*
2678 * click_count is so that if you click on two
2679 * different rows we don't send a double click signal.
2680 */
2681
2682 if (eti->click_count >= 2) {
2683
2684 event_x_item = event_x_win;
2685 event_y_item = event_y_win;
2686
2687 gnome_canvas_item_w2i (
2688 item, &event_x_item, &event_y_item);
2689
2690 g_object_get (
2691 eti->selection,
2692 "cursor_row", &model_row,
2693 "cursor_col", &model_col,
2694 NULL);
2695
2696 /* Clone the event and alter its position. */
2697 event_copy = gdk_event_copy (event);
2698 event_copy->button.x = event_x_item -
2699 e_table_header_col_diff (
2700 eti->header, 0,
2701 model_to_view_col (eti, model_col));
2702 event_copy->button.y = event_y_item -
2703 e_table_item_row_diff (
2704 eti, 0,
2705 model_to_view_row (eti, model_row));
2706
2707 if (event_button == 1) {
2708 if (eti->maybe_in_drag) {
2709 eti->maybe_in_drag = FALSE;
2710 if (!eti->maybe_did_something)
2711 e_selection_model_do_something (E_SELECTION_MODEL (eti->selection), eti->drag_row, eti->drag_col, eti->drag_state);
2712 }
2713 if (eti->in_drag) {
2714 eti->in_drag = FALSE;
2715 }
2716 if (eti_editing (eti))
2717 e_table_item_leave_edit_ (eti);
2718
2719 }
2720
2721 if (eti->grabbed_count > 0) {
2722 d (g_print ("%s: eti_ungrab\n", G_STRFUNC));
2723 eti_ungrab (eti, event_time);
2724 }
2725
2726 if (model_row != -1 && model_col != -1) {
2727 g_signal_emit (
2728 eti, eti_signals[DOUBLE_CLICK], 0,
2729 model_row, model_col, event_copy);
2730 }
2731
2732 gdk_event_free (event_copy);
2733 }
2734 break;
2735 }
2736 case GDK_MOTION_NOTIFY: {
2737 gint col, row, flags;
2738 gdouble x1, y1;
2739 gint cursor_col, cursor_row;
2740
2741 event_x_item = event_x_win;
2742 event_y_item = event_y_win;
2743
2744 gnome_canvas_item_w2i (item, &event_x_item, &event_y_item);
2745
2746 if (eti->maybe_in_drag) {
2747 if (gtk_drag_check_threshold (GTK_WIDGET (item->canvas), eti->drag_x, eti->drag_y, event_x_item, event_y_item)) {
2748 gboolean drag_handled;
2749
2750 eti->maybe_in_drag = 0;
2751
2752 /* Clone the event and
2753 * alter its position. */
2754 event_copy = gdk_event_copy (event);
2755 event_copy->motion.x = event_x_item;
2756 event_copy->motion.y = event_y_item;
2757
2758 g_signal_emit (
2759 eti, eti_signals[START_DRAG], 0,
2760 eti->drag_row, eti->drag_col,
2761 event_copy, &drag_handled);
2762
2763 gdk_event_free (event_copy);
2764
2765 if (drag_handled)
2766 eti->in_drag = 1;
2767 else
2768 eti->in_drag = 0;
2769 }
2770 }
2771
2772 if (!find_cell (eti, event_x_item, event_y_item, &col, &row, &x1, &y1))
2773 return TRUE;
2774
2775 if (eti->motion_row != -1 && eti->motion_col != -1 &&
2776 (row != eti->motion_row || col != eti->motion_col)) {
2777 GdkEvent *cross = gdk_event_new (GDK_LEAVE_NOTIFY);
2778 cross->crossing.time = event_time;
2779 return_val = eti_e_cell_event (
2780 eti, eti->cell_views[eti->motion_col],
2781 cross,
2782 view_to_model_col (eti, eti->motion_col),
2783 eti->motion_col, eti->motion_row, 0);
2784 }
2785
2786 eti->motion_row = row;
2787 eti->motion_col = col;
2788
2789 g_object_get (
2790 eti->selection,
2791 "cursor_row", &cursor_row,
2792 "cursor_col", &cursor_col,
2793 NULL);
2794
2795 flags = 0;
2796 if (cursor_row == view_to_model_row (eti, row) && cursor_col == view_to_model_col (eti, col)) {
2797 flags = E_CELL_EDITING | E_CELL_CURSOR;
2798 }
2799
2800 ecell_view = eti->cell_views[col];
2801
2802 /* Clone the event and alter its position. */
2803 event_copy = gdk_event_copy (event);
2804 event_copy->motion.x = x1;
2805 event_copy->motion.y = y1;
2806
2807 return_val = eti_e_cell_event (
2808 eti, ecell_view, event_copy,
2809 view_to_model_col (eti, col), col, row, flags);
2810
2811 gdk_event_free (event_copy);
2812
2813 break;
2814 }
2815
2816 case GDK_KEY_PRESS: {
2817 gint cursor_row, cursor_col;
2818 gint handled = TRUE;
2819
2820 d (g_print ("%s: GDK_KEY_PRESS received, keyval: %d\n", G_STRFUNC, (gint) e->key.keyval));
2821
2822 g_object_get (
2823 eti->selection,
2824 "cursor_row", &cursor_row,
2825 "cursor_col", &cursor_col,
2826 NULL);
2827
2828 if (cursor_row == -1 && cursor_col == -1)
2829 return FALSE;
2830
2831 eti->in_key_press = TRUE;
2832
2833 switch (event_keyval) {
2834 case GDK_KEY_Left:
2835 case GDK_KEY_KP_Left:
2836 if (eti_editing (eti)) {
2837 handled = FALSE;
2838 break;
2839 }
2840
2841 g_signal_emit (
2842 eti, eti_signals[KEY_PRESS], 0,
2843 model_to_view_row (eti, cursor_row),
2844 cursor_col, event, &return_val);
2845 if ((!return_val) &&
2846 (atk_get_root () || eti->cursor_mode != E_CURSOR_LINE) &&
2847 cursor_col != view_to_model_col (eti, 0))
2848 eti_cursor_move_left (eti);
2849 return_val = 1;
2850 break;
2851
2852 case GDK_KEY_Right:
2853 case GDK_KEY_KP_Right:
2854 if (eti_editing (eti)) {
2855 handled = FALSE;
2856 break;
2857 }
2858
2859 g_signal_emit (
2860 eti, eti_signals[KEY_PRESS], 0,
2861 model_to_view_row (eti, cursor_row),
2862 cursor_col, event, &return_val);
2863 if ((!return_val) &&
2864 (atk_get_root () || eti->cursor_mode != E_CURSOR_LINE) &&
2865 cursor_col != view_to_model_col (eti, eti->cols - 1))
2866 eti_cursor_move_right (eti);
2867 return_val = 1;
2868 break;
2869
2870 case GDK_KEY_Up:
2871 case GDK_KEY_KP_Up:
2872 case GDK_KEY_Down:
2873 case GDK_KEY_KP_Down:
2874 if ((event_state & GDK_MOD1_MASK)
2875 && ((event_keyval == GDK_KEY_Down) || (event_keyval == GDK_KEY_KP_Down))) {
2876 gint view_col = model_to_view_col (eti, cursor_col);
2877
2878 if ((view_col >= 0) && (view_col < eti->cols))
2879 if (eti_e_cell_event (eti, eti->cell_views[view_col], event, cursor_col, view_col, model_to_view_row (eti, cursor_row), E_CELL_CURSOR))
2880 return TRUE;
2881 } else
2882 return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) event);
2883 break;
2884 case GDK_KEY_Home:
2885 case GDK_KEY_KP_Home:
2886 if (eti_editing (eti)) {
2887 handled = FALSE;
2888 break;
2889 }
2890
2891 if (eti->cursor_mode != E_CURSOR_LINE) {
2892 eti_cursor_move (eti, model_to_view_row (eti, cursor_row), 0);
2893 return_val = TRUE;
2894 } else
2895 return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) event);
2896 break;
2897 case GDK_KEY_End:
2898 case GDK_KEY_KP_End:
2899 if (eti_editing (eti)) {
2900 handled = FALSE;
2901 break;
2902 }
2903
2904 if (eti->cursor_mode != E_CURSOR_LINE) {
2905 eti_cursor_move (eti, model_to_view_row (eti, cursor_row), eti->cols - 1);
2906 return_val = TRUE;
2907 } else
2908 return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) event);
2909 break;
2910 case GDK_KEY_Tab:
2911 case GDK_KEY_KP_Tab:
2912 case GDK_KEY_ISO_Left_Tab:
2913 if ((event_state & GDK_CONTROL_MASK) != 0) {
2914 return_val = FALSE;
2915 break;
2916 }
2917 if (eti->cursor_mode == E_CURSOR_SPREADSHEET) {
2918 if ((event_state & GDK_SHIFT_MASK) != 0) {
2919 /* shift tab */
2920 if (cursor_col != view_to_model_col (eti, 0))
2921 eti_cursor_move_left (eti);
2922 else if (cursor_row != view_to_model_row (eti, 0))
2923 eti_cursor_move (eti, model_to_view_row (eti, cursor_row) - 1, eti->cols - 1);
2924 else
2925 return_val = FALSE;
2926 } else {
2927 if (cursor_col != view_to_model_col (eti, eti->cols - 1))
2928 eti_cursor_move_right (eti);
2929 else if (cursor_row != view_to_model_row (eti, eti->rows - 1))
2930 eti_cursor_move (eti, model_to_view_row (eti, cursor_row) + 1, 0);
2931 else
2932 return_val = FALSE;
2933 }
2934 g_object_get (
2935 eti->selection,
2936 "cursor_row", &cursor_row,
2937 "cursor_col", &cursor_col,
2938 NULL);
2939
2940 if (cursor_col >= 0 && cursor_row >= 0 && return_val &&
2941 (!eti_editing (eti)) && e_table_model_is_cell_editable (eti->table_model, cursor_col, model_to_view_row (eti, cursor_row))) {
2942 e_table_item_enter_edit (eti, model_to_view_col (eti, cursor_col), model_to_view_row (eti, cursor_row));
2943 }
2944 break;
2945 } else {
2946 /* Let tab send you to the next widget. */
2947 return_val = FALSE;
2948 break;
2949 }
2950
2951 case GDK_KEY_Return:
2952 case GDK_KEY_KP_Enter:
2953 case GDK_KEY_ISO_Enter:
2954 case GDK_KEY_3270_Enter:
2955 if (eti_editing (eti)) {
2956 ecell_view = eti->cell_views[eti->editing_col];
2957 return_val = eti_e_cell_event (
2958 eti, ecell_view, event,
2959 view_to_model_col (eti, eti->editing_col),
2960 eti->editing_col, eti->editing_row, E_CELL_EDITING | E_CELL_CURSOR | E_CELL_PREEDIT);
2961 if (!return_val)
2962 break;
2963 }
2964 g_signal_emit (
2965 eti, eti_signals[KEY_PRESS], 0,
2966 model_to_view_row (eti, cursor_row),
2967 cursor_col, event, &return_val);
2968 if (!return_val)
2969 return_val = e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) event);
2970 break;
2971
2972 default:
2973 handled = FALSE;
2974 break;
2975 }
2976
2977 if (!handled) {
2978 switch (event_keyval) {
2979 case GDK_KEY_Scroll_Lock:
2980 case GDK_KEY_Sys_Req:
2981 case GDK_KEY_Shift_L:
2982 case GDK_KEY_Shift_R:
2983 case GDK_KEY_Control_L:
2984 case GDK_KEY_Control_R:
2985 case GDK_KEY_Caps_Lock:
2986 case GDK_KEY_Shift_Lock:
2987 case GDK_KEY_Meta_L:
2988 case GDK_KEY_Meta_R:
2989 case GDK_KEY_Alt_L:
2990 case GDK_KEY_Alt_R:
2991 case GDK_KEY_Super_L:
2992 case GDK_KEY_Super_R:
2993 case GDK_KEY_Hyper_L:
2994 case GDK_KEY_Hyper_R:
2995 case GDK_KEY_ISO_Lock:
2996 break;
2997
2998 default:
2999 if (!eti_editing (eti)) {
3000 gint col, row;
3001 row = model_to_view_row (eti, cursor_row);
3002 col = model_to_view_col (eti, cursor_col);
3003 if (col != -1 && row != -1 && e_table_model_is_cell_editable (eti->table_model, cursor_col, row)) {
3004 e_table_item_enter_edit (eti, col, row);
3005 }
3006 }
3007 if (!eti_editing (eti)) {
3008 g_signal_emit (
3009 eti, eti_signals[KEY_PRESS], 0,
3010 model_to_view_row (eti, cursor_row),
3011 cursor_col, event, &return_val);
3012 if (!return_val)
3013 e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) event);
3014 } else {
3015 ecell_view = eti->cell_views[eti->editing_col];
3016 return_val = eti_e_cell_event (
3017 eti, ecell_view, event,
3018 view_to_model_col (eti, eti->editing_col),
3019 eti->editing_col, eti->editing_row, E_CELL_EDITING | E_CELL_CURSOR);
3020 if (!return_val)
3021 e_selection_model_key_press (E_SELECTION_MODEL (eti->selection), (GdkEventKey *) event);
3022 }
3023 break;
3024 }
3025 }
3026 eti->in_key_press = FALSE;
3027 break;
3028 }
3029
3030 case GDK_KEY_RELEASE: {
3031 gint cursor_row, cursor_col;
3032
3033 d (g_print ("%s: GDK_KEY_RELEASE received, keyval: %d\n", G_STRFUNC, (gint) event_keyval));
3034
3035 g_object_get (
3036 eti->selection,
3037 "cursor_row", &cursor_row,
3038 "cursor_col", &cursor_col,
3039 NULL);
3040
3041 if (cursor_col == -1)
3042 return FALSE;
3043
3044 if (eti_editing (eti)) {
3045 ecell_view = eti->cell_views[eti->editing_col];
3046 return_val = eti_e_cell_event (
3047 eti, ecell_view, event,
3048 view_to_model_col (eti, eti->editing_col),
3049 eti->editing_col, eti->editing_row, E_CELL_EDITING | E_CELL_CURSOR);
3050 }
3051 break;
3052 }
3053
3054 case GDK_LEAVE_NOTIFY:
3055 d (leave = TRUE);
3056 case GDK_ENTER_NOTIFY:
3057 d (g_print ("%s: %s received\n", G_STRFUNC, leave ? "GDK_LEAVE_NOTIFY" : "GDK_ENTER_NOTIFY"));
3058 if (eti->motion_row != -1 && eti->motion_col != -1)
3059 return_val = eti_e_cell_event (
3060 eti, eti->cell_views[eti->motion_col],
3061 event,
3062 view_to_model_col (eti, eti->motion_col),
3063 eti->motion_col, eti->motion_row, 0);
3064 eti->motion_row = -1;
3065 eti->motion_col = -1;
3066
3067 break;
3068
3069 case GDK_FOCUS_CHANGE:
3070 d (g_print ("%s: GDK_FOCUS_CHANGE received, %s\n", G_STRFUNC, e->focus_change.in ? "in": "out"));
3071 if (event->focus_change.in) {
3072 if (eti->save_row != -1 &&
3073 eti->save_col != -1 &&
3074 !eti_editing (eti) &&
3075 e_table_model_is_cell_editable (eti->table_model, view_to_model_col (eti, eti->save_col), eti->save_row)) {
3076 e_table_item_enter_edit (eti, eti->save_col, eti->save_row);
3077 e_cell_load_state (
3078 eti->cell_views[eti->editing_col], view_to_model_col (eti, eti->save_col),
3079 eti->save_col, eti->save_row, eti->edit_ctx, eti->save_state);
3080 eti_free_save_state (eti);
3081 }
3082 } else {
3083 if (eti_editing (eti)) {
3084 eti_free_save_state (eti);
3085
3086 eti->save_row = eti->editing_row;
3087 eti->save_col = eti->editing_col;
3088 eti->save_state = e_cell_save_state (
3089 eti->cell_views[eti->editing_col], view_to_model_col (eti, eti->editing_col),
3090 eti->editing_col, eti->editing_row, eti->edit_ctx);
3091 e_table_item_leave_edit_(eti);
3092 }
3093 }
3094 return_val = FALSE;
3095 break;
3096
3097 default:
3098 return_val = FALSE;
3099 break;
3100 }
3101 /* d(g_print("%s: returning: %s\n", G_STRFUNC, return_val?"true":"false"));*/
3102
3103 return return_val;
3104 }
3105
3106 static void
eti_style_updated(ETableItem * eti)3107 eti_style_updated (ETableItem *eti)
3108 {
3109 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3110
3111 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3112 return;
3113
3114 if (eti->cell_views_realized) {
3115 gint i;
3116 gint n_cells = eti->n_cells;
3117
3118 for (i = 0; i < n_cells; i++) {
3119 e_cell_style_updated (eti->cell_views[i]);
3120 }
3121 }
3122
3123 eti->needs_compute_height = 1;
3124 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (eti));
3125 eti->needs_redraw = 1;
3126 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
3127
3128 free_height_cache (eti);
3129
3130 eti_idle_maybe_show_cursor (eti);
3131 }
3132
3133 static void
e_table_item_class_init(ETableItemClass * class)3134 e_table_item_class_init (ETableItemClass *class)
3135 {
3136 GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (class);
3137 GObjectClass *object_class = G_OBJECT_CLASS (class);
3138
3139 g_type_class_add_private (class, sizeof (ETableItemPrivate));
3140
3141 object_class->dispose = eti_dispose;
3142 object_class->set_property = eti_set_property;
3143 object_class->get_property = eti_get_property;
3144
3145 item_class->update = eti_update;
3146 item_class->realize = eti_realize;
3147 item_class->unrealize = eti_unrealize;
3148 item_class->draw = eti_draw;
3149 item_class->point = eti_point;
3150 item_class->event = eti_event;
3151
3152 class->cursor_change = NULL;
3153 class->cursor_activated = NULL;
3154 class->double_click = NULL;
3155 class->right_click = NULL;
3156 class->click = NULL;
3157 class->key_press = NULL;
3158 class->start_drag = NULL;
3159 class->style_updated = eti_style_updated;
3160 class->selection_model_removed = NULL;
3161 class->selection_model_added = NULL;
3162
3163 g_object_class_install_property (
3164 object_class,
3165 PROP_TABLE_HEADER,
3166 g_param_spec_object (
3167 "ETableHeader",
3168 "Table header",
3169 "Table header",
3170 E_TYPE_TABLE_HEADER,
3171 G_PARAM_WRITABLE));
3172
3173 g_object_class_install_property (
3174 object_class,
3175 PROP_TABLE_MODEL,
3176 g_param_spec_object (
3177 "ETableModel",
3178 "Table model",
3179 "Table model",
3180 E_TYPE_TABLE_MODEL,
3181 G_PARAM_WRITABLE));
3182
3183 g_object_class_install_property (
3184 object_class,
3185 PROP_SELECTION_MODEL,
3186 g_param_spec_object (
3187 "selection_model",
3188 "Selection model",
3189 "Selection model",
3190 E_TYPE_SELECTION_MODEL,
3191 G_PARAM_WRITABLE));
3192
3193 g_object_class_install_property (
3194 object_class,
3195 PROP_TABLE_ALTERNATING_ROW_COLORS,
3196 g_param_spec_boolean (
3197 "alternating_row_colors",
3198 "Alternating Row Colors",
3199 "Alternating Row Colors",
3200 FALSE,
3201 G_PARAM_WRITABLE));
3202
3203 g_object_class_install_property (
3204 object_class,
3205 PROP_TABLE_HORIZONTAL_DRAW_GRID,
3206 g_param_spec_boolean (
3207 "horizontal_draw_grid",
3208 "Horizontal Draw Grid",
3209 "Horizontal Draw Grid",
3210 FALSE,
3211 G_PARAM_WRITABLE));
3212
3213 g_object_class_install_property (
3214 object_class,
3215 PROP_TABLE_VERTICAL_DRAW_GRID,
3216 g_param_spec_boolean (
3217 "vertical_draw_grid",
3218 "Vertical Draw Grid",
3219 "Vertical Draw Grid",
3220 FALSE,
3221 G_PARAM_WRITABLE));
3222
3223 g_object_class_install_property (
3224 object_class,
3225 PROP_TABLE_DRAW_FOCUS,
3226 g_param_spec_boolean (
3227 "drawfocus",
3228 "Draw focus",
3229 "Draw focus",
3230 FALSE,
3231 G_PARAM_WRITABLE));
3232
3233 g_object_class_install_property (
3234 object_class,
3235 PROP_CURSOR_MODE,
3236 g_param_spec_int (
3237 "cursor_mode",
3238 "Cursor mode",
3239 "Cursor mode",
3240 E_CURSOR_LINE,
3241 E_CURSOR_SPREADSHEET,
3242 E_CURSOR_LINE,
3243 G_PARAM_WRITABLE));
3244
3245 g_object_class_install_property (
3246 object_class,
3247 PROP_LENGTH_THRESHOLD,
3248 g_param_spec_int (
3249 "length_threshold",
3250 "Length Threshold",
3251 "Length Threshold",
3252 -1, G_MAXINT, 0,
3253 G_PARAM_WRITABLE));
3254
3255 g_object_class_install_property (
3256 object_class,
3257 PROP_MINIMUM_WIDTH,
3258 g_param_spec_double (
3259 "minimum_width",
3260 "Minimum width",
3261 "Minimum Width",
3262 0.0, G_MAXDOUBLE, 0.0,
3263 G_PARAM_READWRITE));
3264
3265 g_object_class_install_property (
3266 object_class,
3267 PROP_WIDTH,
3268 g_param_spec_double (
3269 "width",
3270 "Width",
3271 "Width",
3272 0.0, G_MAXDOUBLE, 0.0,
3273 G_PARAM_READWRITE));
3274
3275 g_object_class_install_property (
3276 object_class,
3277 PROP_HEIGHT,
3278 g_param_spec_double (
3279 "height",
3280 "Height",
3281 "Height",
3282 0.0, G_MAXDOUBLE, 0.0,
3283 G_PARAM_READABLE));
3284
3285 g_object_class_install_property (
3286 object_class,
3287 PROP_CURSOR_ROW,
3288 g_param_spec_int (
3289 "cursor_row",
3290 "Cursor row",
3291 "Cursor row",
3292 0, G_MAXINT, 0,
3293 G_PARAM_READWRITE));
3294
3295 g_object_class_install_property (
3296 object_class,
3297 PROP_UNIFORM_ROW_HEIGHT,
3298 g_param_spec_boolean (
3299 "uniform_row_height",
3300 "Uniform row height",
3301 "Uniform row height",
3302 FALSE,
3303 G_PARAM_READWRITE));
3304
3305 g_object_class_install_property (
3306 object_class,
3307 PROP_IS_EDITING,
3308 g_param_spec_boolean (
3309 "is-editing",
3310 "Whether is in an editing mode",
3311 "Whether is in an editing mode",
3312 FALSE,
3313 G_PARAM_READABLE));
3314
3315 eti_signals[CURSOR_CHANGE] = g_signal_new (
3316 "cursor_change",
3317 G_OBJECT_CLASS_TYPE (object_class),
3318 G_SIGNAL_RUN_LAST,
3319 G_STRUCT_OFFSET (ETableItemClass, cursor_change),
3320 NULL, NULL,
3321 g_cclosure_marshal_VOID__INT,
3322 G_TYPE_NONE, 1,
3323 G_TYPE_INT);
3324
3325 eti_signals[CURSOR_ACTIVATED] = g_signal_new (
3326 "cursor_activated",
3327 G_OBJECT_CLASS_TYPE (object_class),
3328 G_SIGNAL_RUN_LAST,
3329 G_STRUCT_OFFSET (ETableItemClass, cursor_activated),
3330 NULL, NULL,
3331 g_cclosure_marshal_VOID__INT,
3332 G_TYPE_NONE, 1,
3333 G_TYPE_INT);
3334
3335 eti_signals[DOUBLE_CLICK] = g_signal_new (
3336 "double_click",
3337 G_OBJECT_CLASS_TYPE (object_class),
3338 G_SIGNAL_RUN_LAST,
3339 G_STRUCT_OFFSET (ETableItemClass, double_click),
3340 NULL, NULL,
3341 e_marshal_VOID__INT_INT_BOXED,
3342 G_TYPE_NONE, 3,
3343 G_TYPE_INT,
3344 G_TYPE_INT,
3345 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3346
3347 eti_signals[START_DRAG] = g_signal_new (
3348 "start_drag",
3349 G_OBJECT_CLASS_TYPE (object_class),
3350 G_SIGNAL_RUN_LAST,
3351 G_STRUCT_OFFSET (ETableItemClass, start_drag),
3352 g_signal_accumulator_true_handled, NULL,
3353 e_marshal_BOOLEAN__INT_INT_BOXED,
3354 G_TYPE_BOOLEAN, 3,
3355 G_TYPE_INT,
3356 G_TYPE_INT,
3357 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3358
3359 eti_signals[RIGHT_CLICK] = g_signal_new (
3360 "right_click",
3361 G_OBJECT_CLASS_TYPE (object_class),
3362 G_SIGNAL_RUN_LAST,
3363 G_STRUCT_OFFSET (ETableItemClass, right_click),
3364 g_signal_accumulator_true_handled, NULL,
3365 e_marshal_BOOLEAN__INT_INT_BOXED,
3366 G_TYPE_BOOLEAN, 3,
3367 G_TYPE_INT,
3368 G_TYPE_INT,
3369 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3370
3371 eti_signals[CLICK] = g_signal_new (
3372 "click",
3373 G_OBJECT_CLASS_TYPE (object_class),
3374 G_SIGNAL_RUN_LAST,
3375 G_STRUCT_OFFSET (ETableItemClass, click),
3376 g_signal_accumulator_true_handled, NULL,
3377 e_marshal_BOOLEAN__INT_INT_BOXED,
3378 G_TYPE_BOOLEAN, 3,
3379 G_TYPE_INT,
3380 G_TYPE_INT,
3381 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3382
3383 eti_signals[KEY_PRESS] = g_signal_new (
3384 "key_press",
3385 G_OBJECT_CLASS_TYPE (object_class),
3386 G_SIGNAL_RUN_LAST,
3387 G_STRUCT_OFFSET (ETableItemClass, key_press),
3388 g_signal_accumulator_true_handled, NULL,
3389 e_marshal_BOOLEAN__INT_INT_BOXED,
3390 G_TYPE_BOOLEAN, 3,
3391 G_TYPE_INT,
3392 G_TYPE_INT,
3393 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
3394
3395 eti_signals[STYLE_UPDATED] = g_signal_new (
3396 "style_updated",
3397 G_OBJECT_CLASS_TYPE (object_class),
3398 G_SIGNAL_RUN_LAST,
3399 G_STRUCT_OFFSET (ETableItemClass, style_updated),
3400 NULL, NULL,
3401 g_cclosure_marshal_VOID__VOID,
3402 G_TYPE_NONE, 0);
3403
3404 eti_signals[SELECTION_MODEL_REMOVED] = g_signal_new (
3405 "selection_model_removed",
3406 G_TYPE_FROM_CLASS (object_class),
3407 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
3408 G_STRUCT_OFFSET (ETableItemClass, selection_model_removed),
3409 NULL, NULL,
3410 g_cclosure_marshal_VOID__POINTER,
3411 G_TYPE_NONE, 1,
3412 G_TYPE_POINTER);
3413
3414 eti_signals[SELECTION_MODEL_ADDED] = g_signal_new (
3415 "selection_model_added",
3416 G_TYPE_FROM_CLASS (object_class),
3417 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
3418 G_STRUCT_OFFSET (ETableItemClass, selection_model_added),
3419 NULL, NULL,
3420 g_cclosure_marshal_VOID__POINTER,
3421 G_TYPE_NONE, 1,
3422 G_TYPE_POINTER);
3423
3424 eti_signals[GET_BG_COLOR] = g_signal_new (
3425 "get-bg-color",
3426 G_OBJECT_CLASS_TYPE (object_class),
3427 G_SIGNAL_RUN_LAST,
3428 G_STRUCT_OFFSET (ETableItemClass, get_bg_color),
3429 NULL, NULL,
3430 NULL,
3431 G_TYPE_BOOLEAN, 3, /* return TRUE when set */
3432 G_TYPE_INT, /* row */
3433 G_TYPE_INT, /* col */
3434 G_TYPE_POINTER /* GdkRGBA *background, but cannot be passed as
3435 boxed, because it's used as (inout) argument */);
3436
3437 /* A11y Init */
3438 gal_a11y_e_table_item_init ();
3439 }
3440
3441 /**
3442 * e_table_item_set_cursor:
3443 * @eti: %ETableItem which will have the cursor set.
3444 * @col: Column to select. -1 means the last column.
3445 * @row: Row to select. -1 means the last row.
3446 *
3447 * This routine sets the cursor of the %ETableItem canvas item.
3448 */
3449 void
e_table_item_set_cursor(ETableItem * eti,gint col,gint row)3450 e_table_item_set_cursor (ETableItem *eti,
3451 gint col,
3452 gint row)
3453 {
3454 e_table_item_focus (eti, col, view_to_model_row (eti, row), 0);
3455 }
3456
3457 static void
e_table_item_focus(ETableItem * eti,gint col,gint row,GdkModifierType state)3458 e_table_item_focus (ETableItem *eti,
3459 gint col,
3460 gint row,
3461 GdkModifierType state)
3462 {
3463 g_return_if_fail (eti != NULL);
3464 g_return_if_fail (E_IS_TABLE_ITEM (eti));
3465
3466 if (row == -1) {
3467 row = view_to_model_row (eti, eti->rows - 1);
3468 }
3469
3470 if (col == -1) {
3471 col = eti->cols - 1;
3472 }
3473
3474 if (row != -1) {
3475 e_selection_model_do_something (
3476 E_SELECTION_MODEL (eti->selection),
3477 row, col, state);
3478 }
3479 }
3480
3481 /**
3482 * e_table_item_get_focused_column:
3483 * @eti: %ETableItem which will have the cursor retrieved.
3484 *
3485 * This routine gets the cursor of the %ETableItem canvas item.
3486 *
3487 * Returns: The current cursor column.
3488 */
3489 gint
e_table_item_get_focused_column(ETableItem * eti)3490 e_table_item_get_focused_column (ETableItem *eti)
3491 {
3492 gint cursor_col;
3493
3494 g_return_val_if_fail (eti != NULL, -1);
3495 g_return_val_if_fail (E_IS_TABLE_ITEM (eti), -1);
3496
3497 g_object_get (
3498 eti->selection,
3499 "cursor_col", &cursor_col,
3500 NULL);
3501
3502 return cursor_col;
3503 }
3504
3505 static void
eti_cursor_change(ESelectionModel * selection,gint row,gint col,ETableItem * eti)3506 eti_cursor_change (ESelectionModel *selection,
3507 gint row,
3508 gint col,
3509 ETableItem *eti)
3510 {
3511 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3512 gint view_row;
3513
3514 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3515 return;
3516
3517 view_row = model_to_view_row (eti, row);
3518
3519 if (eti->old_cursor_row != -1 && view_row != eti->old_cursor_row)
3520 e_table_item_redraw_row (eti, eti->old_cursor_row);
3521
3522 if (view_row == -1) {
3523 e_table_item_leave_edit_(eti);
3524 eti->old_cursor_row = -1;
3525 return;
3526 }
3527
3528 if (!e_table_model_has_change_pending (eti->table_model)) {
3529 if (!eti->in_key_press) {
3530 eti_maybe_show_cursor (eti, DOUBLE_CLICK_TIME + 10);
3531 } else {
3532 eti_maybe_show_cursor (eti, 0);
3533 }
3534 }
3535
3536 e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (eti), FALSE);
3537 if (eti_editing (eti))
3538 e_table_item_leave_edit_(eti);
3539
3540 g_signal_emit (eti, eti_signals[CURSOR_CHANGE], 0, view_row);
3541
3542 e_table_item_redraw_row (eti, view_row);
3543
3544 eti->old_cursor_row = view_row;
3545 }
3546
3547 static void
eti_cursor_activated(ESelectionModel * selection,gint row,gint col,ETableItem * eti)3548 eti_cursor_activated (ESelectionModel *selection,
3549 gint row,
3550 gint col,
3551 ETableItem *eti)
3552 {
3553 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3554 gint view_row;
3555 gint view_col;
3556
3557 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3558 return;
3559
3560 view_row = model_to_view_row (eti, row);
3561 view_col = model_to_view_col (eti, col);
3562
3563 if (view_row != -1 && view_col != -1) {
3564 if (!e_table_model_has_change_pending (eti->table_model)) {
3565 if (!eti->in_key_press) {
3566 eti_show_cursor (eti, DOUBLE_CLICK_TIME + 10);
3567 } else {
3568 eti_show_cursor (eti, 0);
3569 }
3570 eti_check_cursor_bounds (eti);
3571 }
3572 }
3573
3574 if (eti_editing (eti))
3575 e_table_item_leave_edit_(eti);
3576
3577 if (view_row != -1)
3578 g_signal_emit (
3579 eti, eti_signals[CURSOR_ACTIVATED], 0, view_row);
3580 }
3581
3582 static void
eti_selection_change(ESelectionModel * selection,ETableItem * eti)3583 eti_selection_change (ESelectionModel *selection,
3584 ETableItem *eti)
3585 {
3586 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3587
3588 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3589 return;
3590
3591 eti->needs_redraw = TRUE;
3592 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (eti));
3593 }
3594
3595 static void
eti_selection_row_change(ESelectionModel * selection,gint row,ETableItem * eti)3596 eti_selection_row_change (ESelectionModel *selection,
3597 gint row,
3598 ETableItem *eti)
3599 {
3600 GnomeCanvasItem *item = GNOME_CANVAS_ITEM (eti);
3601
3602 if (!(item->flags & GNOME_CANVAS_ITEM_REALIZED))
3603 return;
3604
3605 if (!eti->needs_redraw) {
3606 e_table_item_redraw_row (eti, model_to_view_row (eti, row));
3607 }
3608 }
3609
3610 /**
3611 * e_table_item_enter_edit
3612 * @eti: %ETableItem which will start being edited
3613 * @col: The view col to edit.
3614 * @row: The view row to edit.
3615 *
3616 * This routine starts the given %ETableItem editing at the given view
3617 * column and row.
3618 */
3619 void
e_table_item_enter_edit(ETableItem * eti,gint col,gint row)3620 e_table_item_enter_edit (ETableItem *eti,
3621 gint col,
3622 gint row)
3623 {
3624 g_return_if_fail (eti != NULL);
3625 g_return_if_fail (E_IS_TABLE_ITEM (eti));
3626
3627 d (g_print ("%s: %d, %d, eti_editing() = %s\n", G_STRFUNC, col, row, eti_editing (eti)?"true":"false"));
3628
3629 if (eti_editing (eti))
3630 e_table_item_leave_edit_(eti);
3631
3632 eti->editing_col = col;
3633 eti->editing_row = row;
3634
3635 if (col >= 0) {
3636 eti->edit_ctx = e_cell_enter_edit (eti->cell_views[col], view_to_model_col (eti, col), col, row);
3637
3638 g_object_notify (G_OBJECT (eti), "is-editing");
3639 }
3640 }
3641
3642 /**
3643 * e_table_item_leave_edit_
3644 * @eti: %ETableItem which will stop being edited
3645 *
3646 * This routine stops the given %ETableItem from editing.
3647 */
3648 void
e_table_item_leave_edit(ETableItem * eti)3649 e_table_item_leave_edit (ETableItem *eti)
3650 {
3651 gint col, row;
3652 gpointer edit_ctx;
3653
3654 g_return_if_fail (eti != NULL);
3655 g_return_if_fail (E_IS_TABLE_ITEM (eti));
3656
3657 d (g_print ("%s: eti_editing() = %s\n", G_STRFUNC, eti_editing (eti)?"true":"false"));
3658
3659 if (!eti_editing (eti))
3660 return;
3661
3662 col = eti->editing_col;
3663 row = eti->editing_row;
3664 edit_ctx = eti->edit_ctx;
3665
3666 eti->editing_col = -1;
3667 eti->editing_row = -1;
3668 eti->edit_ctx = NULL;
3669
3670 e_cell_leave_edit (
3671 eti->cell_views[col],
3672 view_to_model_col (eti, col),
3673 col, row, edit_ctx);
3674
3675 g_object_notify (G_OBJECT (eti), "is-editing");
3676 }
3677
3678 /**
3679 * e_table_item_compute_location
3680 * @eti: %ETableItem to look in.
3681 * @x: A pointer to the x location to find in the %ETableItem.
3682 * @y: A pointer to the y location to find in the %ETableItem.
3683 * @row: A pointer to the location to store the found row in.
3684 * @col: A pointer to the location to store the found col in.
3685 *
3686 * This routine locates the pixel location (*x, *y) in the
3687 * %ETableItem. If that location is in the %ETableItem, *row and *col
3688 * are set to the view row and column where it was found. If that
3689 * location is not in the %ETableItem, the height of the %ETableItem
3690 * is removed from the value y points to.
3691 */
3692 void
e_table_item_compute_location(ETableItem * eti,gint * x,gint * y,gint * row,gint * col)3693 e_table_item_compute_location (ETableItem *eti,
3694 gint *x,
3695 gint *y,
3696 gint *row,
3697 gint *col)
3698 {
3699 /* Save the grabbed row but make sure that we don't get flawed
3700 * results because the cursor is grabbed. */
3701 gint grabbed_row = eti->grabbed_row;
3702 eti->grabbed_row = -1;
3703
3704 if (!find_cell (eti, *x, *y, col, row, NULL, NULL)) {
3705 *y -= eti->height;
3706 }
3707
3708 eti->grabbed_row = grabbed_row;
3709 }
3710
3711 /**
3712 * e_table_item_compute_mouse_over:
3713 * Similar to e_table_item_compute_location, only here recalculating
3714 * the position inside the item too.
3715 **/
3716 void
e_table_item_compute_mouse_over(ETableItem * eti,gint x,gint y,gint * row,gint * col)3717 e_table_item_compute_mouse_over (ETableItem *eti,
3718 gint x,
3719 gint y,
3720 gint *row,
3721 gint *col)
3722 {
3723 gdouble realx, realy;
3724 /* Save the grabbed row but make sure that we don't get flawed
3725 * results because the cursor is grabbed. */
3726 gint grabbed_row = eti->grabbed_row;
3727 eti->grabbed_row = -1;
3728
3729 realx = x;
3730 realy = y;
3731
3732 gnome_canvas_item_w2i (GNOME_CANVAS_ITEM (eti), &realx, &realy);
3733
3734 if (!find_cell (eti, (gint) realx, (gint) realy, col, row, NULL, NULL)) {
3735 *row = -1;
3736 *col = -1;
3737 }
3738
3739 eti->grabbed_row = grabbed_row;
3740 }
3741
3742 void
e_table_item_get_cell_geometry(ETableItem * eti,gint * row,gint * col,gint * x,gint * y,gint * width,gint * height)3743 e_table_item_get_cell_geometry (ETableItem *eti,
3744 gint *row,
3745 gint *col,
3746 gint *x,
3747 gint *y,
3748 gint *width,
3749 gint *height)
3750 {
3751 if (eti->rows > *row) {
3752 if (x)
3753 *x = e_table_header_col_diff (eti->header, 0, *col);
3754 if (y)
3755 *y = e_table_item_row_diff (eti, 0, *row);
3756 if (width)
3757 *width = e_table_header_col_diff (eti->header, *col, *col + 1);
3758 if (height)
3759 *height = ETI_ROW_HEIGHT (eti, *row);
3760 *row = -1;
3761 *col = -1;
3762 } else {
3763 *row -= eti->rows;
3764 }
3765 }
3766
3767 typedef struct {
3768 ETableItem *item;
3769 gint rows_printed;
3770 } ETableItemPrintContext;
3771
3772 static gdouble *
e_table_item_calculate_print_widths(ETableHeader * eth,gdouble width)3773 e_table_item_calculate_print_widths (ETableHeader *eth,
3774 gdouble width)
3775 {
3776 gint i;
3777 gdouble extra;
3778 gdouble expansion;
3779 gint last_resizable = -1;
3780 gdouble scale = 1.0L;
3781 gdouble *widths = g_new (gdouble, e_table_header_count (eth));
3782 /* - 1 to account for the last pixel border. */
3783 extra = width - 1;
3784 expansion = 0;
3785 for (i = 0; i < eth->col_count; i++) {
3786 extra -= eth->columns[i]->min_width * scale;
3787 if (eth->columns[i]->spec->resizable && eth->columns[i]->expansion > 0)
3788 last_resizable = i;
3789 expansion += eth->columns[i]->spec->resizable ? eth->columns[i]->expansion : 0;
3790 widths[i] = eth->columns[i]->min_width * scale;
3791 }
3792 for (i = 0; i <= last_resizable; i++) {
3793 widths[i] += extra * (eth->columns[i]->spec->resizable ? eth->columns[i]->expansion : 0) / expansion;
3794 }
3795
3796 return widths;
3797 }
3798
3799 static gdouble
eti_printed_row_height(ETableItem * eti,gdouble * widths,GtkPrintContext * context,gint row)3800 eti_printed_row_height (ETableItem *eti,
3801 gdouble *widths,
3802 GtkPrintContext *context,
3803 gint row)
3804 {
3805 gint col;
3806 gint cols = eti->cols;
3807 gdouble height = 0;
3808 for (col = 0; col < cols; col++) {
3809 ECellView *ecell_view = eti->cell_views[col];
3810 gdouble this_height = e_cell_print_height (
3811 ecell_view, context, view_to_model_col (eti, col), col, row,
3812 widths[col] - 1);
3813 if (this_height > height)
3814 height = this_height;
3815 }
3816 return height;
3817 }
3818
3819 #define CHECK(x) if((x) == -1) return -1;
3820
3821 static gint
gp_draw_rect(GtkPrintContext * context,gdouble x,gdouble y,gdouble width,gdouble height)3822 gp_draw_rect (GtkPrintContext *context,
3823 gdouble x,
3824 gdouble y,
3825 gdouble width,
3826 gdouble height)
3827 {
3828 cairo_t *cr;
3829 cr = gtk_print_context_get_cairo_context (context);
3830 cairo_save (cr);
3831 cairo_rectangle (cr, x, y, width, height);
3832 cairo_set_line_width (cr, 0.5);
3833 cairo_stroke (cr);
3834 cairo_restore (cr);
3835 return 0;
3836 }
3837
3838 static void
e_table_item_print_page(EPrintable * ep,GtkPrintContext * context,gdouble width,gdouble height,gboolean quantize,ETableItemPrintContext * itemcontext)3839 e_table_item_print_page (EPrintable *ep,
3840 GtkPrintContext *context,
3841 gdouble width,
3842 gdouble height,
3843 gboolean quantize,
3844 ETableItemPrintContext *itemcontext)
3845 {
3846 ETableItem *eti = itemcontext->item;
3847 const gint rows = eti->rows;
3848 const gint cols = eti->cols;
3849 gdouble max_height;
3850 gint rows_printed = itemcontext->rows_printed;
3851 gint row, col, next_page = 0;
3852 gdouble yd = height;
3853 cairo_t *cr;
3854 gdouble *widths;
3855
3856 cr = gtk_print_context_get_cairo_context (context);
3857 max_height = gtk_print_context_get_height (context);
3858 widths = e_table_item_calculate_print_widths (itemcontext->item->header, width);
3859
3860 /*
3861 * Draw cells
3862 */
3863
3864 if (eti->horizontal_draw_grid) {
3865 gp_draw_rect (context, 0, yd, width, 1);
3866 }
3867 yd++;
3868
3869 for (row = rows_printed; row < rows; row++) {
3870 gdouble xd = 1, row_height;
3871 row_height = eti_printed_row_height (eti, widths, context, row);
3872
3873 if (quantize) {
3874 if (yd + row_height + 1 > max_height && row != rows_printed) {
3875 next_page = 1;
3876 break;
3877 }
3878 } else {
3879 if (yd > max_height) {
3880 next_page = 1;
3881 break;
3882 }
3883 }
3884
3885 for (col = 0; col < cols; col++) {
3886 ECellView *ecell_view = eti->cell_views[col];
3887
3888 cairo_save (cr);
3889 cairo_translate (cr, xd, yd);
3890 cairo_rectangle (cr, 0, 0, widths[col] - 1, row_height);
3891 cairo_clip (cr);
3892
3893 e_cell_print (
3894 ecell_view, context,
3895 view_to_model_col (eti, col),
3896 col,
3897 row,
3898 widths[col] - 1,
3899 row_height + 2);
3900
3901 cairo_restore (cr);
3902
3903 xd += widths[col];
3904 }
3905
3906 yd += row_height;
3907 if (eti->horizontal_draw_grid) {
3908 gp_draw_rect (context, 0, yd, width, 1);
3909 }
3910 yd++;
3911 }
3912
3913 itemcontext->rows_printed = row;
3914 if (eti->vertical_draw_grid) {
3915 gdouble xd = 0;
3916 for (col = 0; col < cols; col++) {
3917 gp_draw_rect (context, xd, height, 1, yd - height);
3918 xd += widths[col];
3919 }
3920 gp_draw_rect (context, xd, height, 1, yd - height);
3921 }
3922
3923 if (next_page)
3924 cairo_show_page (cr);
3925
3926 g_free (widths);
3927 }
3928
3929 static gboolean
e_table_item_data_left(EPrintable * ep,ETableItemPrintContext * itemcontext)3930 e_table_item_data_left (EPrintable *ep,
3931 ETableItemPrintContext *itemcontext)
3932 {
3933 ETableItem *item = itemcontext->item;
3934 gint rows_printed = itemcontext->rows_printed;
3935
3936 g_signal_stop_emission_by_name (ep, "data_left");
3937 return rows_printed < item->rows;
3938 }
3939
3940 static void
e_table_item_reset(EPrintable * ep,ETableItemPrintContext * itemcontext)3941 e_table_item_reset (EPrintable *ep,
3942 ETableItemPrintContext *itemcontext)
3943 {
3944 itemcontext->rows_printed = 0;
3945 }
3946
3947 static gdouble
e_table_item_height(EPrintable * ep,GtkPrintContext * context,gdouble width,gdouble max_height,gboolean quantize,ETableItemPrintContext * itemcontext)3948 e_table_item_height (EPrintable *ep,
3949 GtkPrintContext *context,
3950 gdouble width,
3951 gdouble max_height,
3952 gboolean quantize,
3953 ETableItemPrintContext *itemcontext)
3954 {
3955 ETableItem *item = itemcontext->item;
3956 const gint rows = item->rows;
3957 gint rows_printed = itemcontext->rows_printed;
3958 gdouble *widths;
3959 gint row;
3960 gdouble yd = 0;
3961
3962 widths = e_table_item_calculate_print_widths (itemcontext->item->header, width);
3963
3964 /*
3965 * Draw cells
3966 */
3967 yd++;
3968
3969 for (row = rows_printed; row < rows; row++) {
3970 gdouble row_height;
3971
3972 row_height = eti_printed_row_height (item, widths, context, row);
3973 if (quantize) {
3974 if (max_height != -1 && yd + row_height + 1 > max_height && row != rows_printed) {
3975 break;
3976 }
3977 } else {
3978 if (max_height != -1 && yd > max_height) {
3979 break;
3980 }
3981 }
3982
3983 yd += row_height;
3984
3985 yd++;
3986 }
3987
3988 g_free (widths);
3989
3990 if (max_height != -1 && (!quantize) && yd > max_height)
3991 yd = max_height;
3992
3993 g_signal_stop_emission_by_name (ep, "height");
3994 return yd;
3995 }
3996
3997 static gboolean
e_table_item_will_fit(EPrintable * ep,GtkPrintContext * context,gdouble width,gdouble max_height,gboolean quantize,ETableItemPrintContext * itemcontext)3998 e_table_item_will_fit (EPrintable *ep,
3999 GtkPrintContext *context,
4000 gdouble width,
4001 gdouble max_height,
4002 gboolean quantize,
4003 ETableItemPrintContext *itemcontext)
4004 {
4005 ETableItem *item = itemcontext->item;
4006 const gint rows = item->rows;
4007 gint rows_printed = itemcontext->rows_printed;
4008 gdouble *widths;
4009 gint row;
4010 gdouble yd = 0;
4011 gboolean ret_val = TRUE;
4012
4013 widths = e_table_item_calculate_print_widths (itemcontext->item->header, width);
4014
4015 /*
4016 * Draw cells
4017 */
4018 yd++;
4019
4020 for (row = rows_printed; row < rows; row++) {
4021 gdouble row_height;
4022
4023 row_height = eti_printed_row_height (item, widths, context, row);
4024 if (quantize) {
4025 if (max_height != -1 && yd + row_height + 1 > max_height && row != rows_printed) {
4026 ret_val = FALSE;
4027 break;
4028 }
4029 } else {
4030 if (max_height != -1 && yd > max_height) {
4031 ret_val = FALSE;
4032 break;
4033 }
4034 }
4035
4036 yd += row_height;
4037
4038 yd++;
4039 }
4040
4041 g_free (widths);
4042
4043 g_signal_stop_emission_by_name (ep, "will_fit");
4044 return ret_val;
4045 }
4046
4047 static void
e_table_item_printable_destroy(gpointer data,GObject * where_object_was)4048 e_table_item_printable_destroy (gpointer data,
4049 GObject *where_object_was)
4050 {
4051 ETableItemPrintContext *itemcontext = data;
4052
4053 g_object_unref (itemcontext->item);
4054 g_free (itemcontext);
4055 }
4056
4057 /**
4058 * e_table_item_get_printable
4059 * @eti: %ETableItem which will be printed
4060 *
4061 * This routine creates and returns an %EPrintable that can be used to
4062 * print the given %ETableItem.
4063 *
4064 * Returns: The %EPrintable.
4065 */
4066 EPrintable *
e_table_item_get_printable(ETableItem * item)4067 e_table_item_get_printable (ETableItem *item)
4068 {
4069 EPrintable *printable = e_printable_new ();
4070 ETableItemPrintContext *itemcontext;
4071
4072 itemcontext = g_new (ETableItemPrintContext, 1);
4073 itemcontext->item = item;
4074 g_object_ref (item);
4075 itemcontext->rows_printed = 0;
4076
4077 g_signal_connect (
4078 printable, "print_page",
4079 G_CALLBACK (e_table_item_print_page), itemcontext);
4080 g_signal_connect (
4081 printable, "data_left",
4082 G_CALLBACK (e_table_item_data_left), itemcontext);
4083 g_signal_connect (
4084 printable, "reset",
4085 G_CALLBACK (e_table_item_reset), itemcontext);
4086 g_signal_connect (
4087 printable, "height",
4088 G_CALLBACK (e_table_item_height), itemcontext);
4089 g_signal_connect (
4090 printable, "will_fit",
4091 G_CALLBACK (e_table_item_will_fit), itemcontext);
4092
4093 g_object_weak_ref (
4094 G_OBJECT (printable),
4095 e_table_item_printable_destroy, itemcontext);
4096
4097 return printable;
4098 }
4099
4100 /**
4101 * e_table_item_is_editing:
4102 * @eti: an %ETableItem
4103 *
4104 * Returns: Whether the table item is currently editing cell content.
4105 **/
4106 gboolean
e_table_item_is_editing(ETableItem * eti)4107 e_table_item_is_editing (ETableItem *eti)
4108 {
4109 g_return_val_if_fail (E_IS_TABLE_ITEM (eti), FALSE);
4110
4111 return eti_editing (eti);
4112 }
4113
4114 /**
4115 * e_table_item_cursor_scrolled:
4116 * @eti: an %ETableItem
4117 *
4118 * Does necessary recalculations after cursor scrolled, like whether
4119 * the cursor is on screen or not anymore.
4120 **/
4121 void
e_table_item_cursor_scrolled(ETableItem * eti)4122 e_table_item_cursor_scrolled (ETableItem *eti)
4123 {
4124 g_return_if_fail (E_IS_TABLE_ITEM (eti));
4125
4126 eti_check_cursor_bounds (eti);
4127 }
4128
4129 void
e_table_item_cancel_scroll_to_cursor(ETableItem * eti)4130 e_table_item_cancel_scroll_to_cursor (ETableItem *eti)
4131 {
4132 ETableItemPrivate *priv;
4133
4134 g_return_if_fail (E_IS_TABLE_ITEM (eti));
4135
4136 priv = E_TABLE_ITEM_GET_PRIVATE (eti);
4137
4138 if (priv->show_cursor_delay_source) {
4139 g_source_destroy (priv->show_cursor_delay_source);
4140 g_source_unref (priv->show_cursor_delay_source);
4141 priv->show_cursor_delay_source = NULL;
4142 }
4143 }
4144