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