2 * e-cell-popup.c: Popup cell renderer
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 * ECellPopup - an abstract ECell class used to support popup selections like
25 * a GtkCombo widget. It contains a child ECell, e.g. an ECellText, but when
26 * selected it displays an arrow on the right edge which the user can click to
27 * show a popup. Subclasses implement the popup class function to show the
35 #include <gdk/gdkkeysyms.h>
37 #include "gal-a11y-e-cell-popup.h"
38 #include "gal-a11y-e-cell-registry.h"
40 #include "e-cell-popup.h"
41 #include "e-table-item.h"
44 #define E_CELL_POPUP_ARROW_SIZE 16
45 #define E_CELL_POPUP_ARROW_PAD 3
47 static void e_cell_popup_dispose (GObject
*object
);
49 static ECellView
* ecp_new_view (ECell
*ecell
,
50 ETableModel
*table_model
,
51 void *e_table_item_view
);
52 static void ecp_kill_view (ECellView
*ecv
);
53 static void ecp_realize (ECellView
*ecv
);
54 static void ecp_unrealize (ECellView
*ecv
);
55 static void ecp_draw (ECellView
*ecv
,
65 static gint
ecp_event (ECellView
*ecv
,
71 ECellActions
*actions
);
72 static gint
ecp_height (ECellView
*ecv
,
76 static gpointer
ecp_enter_edit (ECellView
*ecv
,
80 static void ecp_leave_edit (ECellView
*ecv
,
85 static void ecp_print (ECellView
*ecv
,
86 GtkPrintContext
*context
,
92 static gdouble
ecp_print_height (ECellView
*ecv
,
93 GtkPrintContext
*context
,
98 static gint
ecp_max_width (ECellView
*ecv
,
101 static gchar
*ecp_get_bg_color (ECellView
*ecell_view
, gint row
);
103 static gint
e_cell_popup_do_popup (ECellPopupView
*ecp_view
,
108 G_DEFINE_TYPE (ECellPopup
, e_cell_popup
, E_TYPE_CELL
)
111 e_cell_popup_class_init (ECellPopupClass
*class)
113 ECellClass
*ecc
= E_CELL_CLASS (class);
115 G_OBJECT_CLASS (class)->dispose
= e_cell_popup_dispose
;
117 ecc
->new_view
= ecp_new_view
;
118 ecc
->kill_view
= ecp_kill_view
;
119 ecc
->realize
= ecp_realize
;
120 ecc
->unrealize
= ecp_unrealize
;
121 ecc
->draw
= ecp_draw
;
122 ecc
->event
= ecp_event
;
123 ecc
->height
= ecp_height
;
124 ecc
->enter_edit
= ecp_enter_edit
;
125 ecc
->leave_edit
= ecp_leave_edit
;
126 ecc
->print
= ecp_print
;
127 ecc
->print_height
= ecp_print_height
;
128 ecc
->max_width
= ecp_max_width
;
129 ecc
->get_bg_color
= ecp_get_bg_color
;
131 gal_a11y_e_cell_registry_add_cell_type (
132 NULL
, E_TYPE_CELL_POPUP
,
133 gal_a11y_e_cell_popup_new
);
137 e_cell_popup_init (ECellPopup
*ecp
)
139 ecp
->popup_shown
= FALSE
;
140 ecp
->popup_model
= NULL
;
146 * Creates a new ECellPopup renderer.
148 * Returns: an ECellPopup object.
151 e_cell_popup_new (void)
153 return g_object_new (E_TYPE_CELL_POPUP
, NULL
);
157 e_cell_popup_dispose (GObject
*object
)
159 ECellPopup
*ecp
= E_CELL_POPUP (object
);
162 g_object_unref (ecp
->child
);
165 G_OBJECT_CLASS (e_cell_popup_parent_class
)->dispose (object
);
169 * ECell::new_view method
172 ecp_new_view (ECell
*ecell
,
173 ETableModel
*table_model
,
174 gpointer e_table_item_view
)
176 ECellPopup
*ecp
= E_CELL_POPUP (ecell
);
177 ECellPopupView
*ecp_view
;
179 /* We must have a child ECell before we create any views. */
180 g_return_val_if_fail (ecp
->child
!= NULL
, NULL
);
182 ecp_view
= g_new0 (ECellPopupView
, 1);
184 ecp_view
->cell_view
.ecell
= g_object_ref (ecell
);
185 ecp_view
->cell_view
.e_table_model
= table_model
;
186 ecp_view
->cell_view
.e_table_item_view
= e_table_item_view
;
187 ecp_view
->cell_view
.kill_view_cb
= NULL
;
188 ecp_view
->cell_view
.kill_view_cb_data
= NULL
;
190 ecp_view
->child_view
= e_cell_new_view (
191 ecp
->child
, table_model
,
194 return (ECellView
*) ecp_view
;
198 * ECell::kill_view method
201 ecp_kill_view (ECellView
*ecv
)
203 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
205 if (E_IS_CELL_POPUP (ecp_view
->cell_view
.ecell
)) {
206 ECellPopup
*ecp
= E_CELL_POPUP (ecp_view
->cell_view
.ecell
);
208 if (ecp
->popup_cell_view
== ecp_view
)
209 ecp
->popup_cell_view
= NULL
;
212 g_clear_object (&ecp_view
->cell_view
.ecell
);
214 if (ecp_view
->cell_view
.kill_view_cb
)
215 ecp_view
->cell_view
.kill_view_cb (
216 ecv
, ecp_view
->cell_view
.kill_view_cb_data
);
218 if (ecp_view
->cell_view
.kill_view_cb_data
)
219 g_list_free (ecp_view
->cell_view
.kill_view_cb_data
);
221 if (ecp_view
->child_view
)
222 e_cell_kill_view (ecp_view
->child_view
);
228 * ECell::realize method
231 ecp_realize (ECellView
*ecv
)
233 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
235 e_cell_realize (ecp_view
->child_view
);
237 if (E_CELL_CLASS (e_cell_popup_parent_class
)->realize
)
238 (* E_CELL_CLASS (e_cell_popup_parent_class
)->realize
) (ecv
);
242 * ECell::unrealize method
245 ecp_unrealize (ECellView
*ecv
)
247 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
249 e_cell_unrealize (ecp_view
->child_view
);
251 if (E_CELL_CLASS (e_cell_popup_parent_class
)->unrealize
)
252 (* E_CELL_CLASS (e_cell_popup_parent_class
)->unrealize
) (ecv
);
259 ecp_draw (ECellView
*ecv
,
270 ECellPopup
*ecp
= E_CELL_POPUP (ecv
->ecell
);
271 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
273 gboolean show_popup_arrow
;
277 canvas
= GTK_WIDGET (GNOME_CANVAS_ITEM (ecv
->e_table_item_view
)->canvas
);
279 /* Display the popup arrow if we are the cursor cell, or the popup
280 * is shown for this cell. */
282 e_table_model_is_cell_editable (
283 ecv
->e_table_model
, model_col
, row
) &&
284 (flags
& E_CELL_CURSOR
||
285 (ecp
->popup_shown
&& ecp
->popup_view_col
== view_col
286 && ecp
->popup_row
== row
287 && ecp
->popup_model
== ((ECellView
*) ecp_view
)->e_table_model
));
289 if (flags
& E_CELL_CURSOR
)
290 ecp
->popup_arrow_shown
= show_popup_arrow
;
292 if (show_popup_arrow
) {
293 GtkStyleContext
*style_context
;
300 ecp_view
->child_view
, cr
, model_col
,
301 view_col
, row
, flags
,
302 x1
, y1
, x2
- E_CELL_POPUP_ARROW_SIZE
, y2
);
304 midpoint_y
= y1
+ ((y2
- y1
+ 1) / 2);
306 arrow_x
= x2
- E_CELL_POPUP_ARROW_SIZE
;
307 arrow_y
= midpoint_y
- E_CELL_POPUP_ARROW_SIZE
/ 2;
308 arrow_size
= E_CELL_POPUP_ARROW_SIZE
;
310 style_context
= gtk_widget_get_style_context (canvas
);
312 gtk_style_context_save (style_context
);
314 gtk_style_context_add_class (
315 style_context
, GTK_STYLE_CLASS_CELL
);
318 gtk_render_background (
322 (gdouble
) arrow_size
,
323 (gdouble
) arrow_size
);
326 arrow_x
+= E_CELL_POPUP_ARROW_PAD
;
327 arrow_y
+= E_CELL_POPUP_ARROW_PAD
;
328 arrow_size
-= (E_CELL_POPUP_ARROW_PAD
* 2);
332 style_context
, cr
, G_PI
,
335 (gdouble
) arrow_size
);
338 gtk_style_context_restore (style_context
);
341 ecp_view
->child_view
, cr
, model_col
,
342 view_col
, row
, flags
, x1
, y1
, x2
, y2
);
349 * ECell::event method
352 ecp_event (ECellView
*ecv
,
358 ECellActions
*actions
)
360 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
361 ECellPopup
*ecp
= E_CELL_POPUP (ecp_view
->cell_view
.ecell
);
362 ETableItem
*eti
= E_TABLE_ITEM (ecv
->e_table_item_view
);
365 switch (event
->type
) {
366 case GDK_BUTTON_PRESS
:
367 if (e_table_model_is_cell_editable (ecv
->e_table_model
, model_col
, row
) &&
368 flags
& E_CELL_CURSOR
369 && ecp
->popup_arrow_shown
) {
370 width
= e_table_header_col_diff (
371 eti
->header
, view_col
,
374 /* FIXME: The event coords seem to be relative to the
375 * text within the cell, so we have to add 4. */
376 if (event
->button
.x
+ 4 >= width
- E_CELL_POPUP_ARROW_SIZE
) {
377 return e_cell_popup_do_popup (ecp_view
, event
, row
, view_col
);
382 if (e_table_model_is_cell_editable (ecv
->e_table_model
, model_col
, row
) &&
383 event
->key
.state
& GDK_MOD1_MASK
384 && event
->key
.keyval
== GDK_KEY_Down
) {
385 return e_cell_popup_do_popup (ecp_view
, event
, row
, view_col
);
392 return e_cell_event (
393 ecp_view
->child_view
, event
, model_col
, view_col
,
394 row
, flags
, actions
);
398 * ECell::height method
401 ecp_height (ECellView
*ecv
,
406 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
408 return e_cell_height (ecp_view
->child_view
, model_col
, view_col
, row
);
412 * ECellView::enter_edit method
415 ecp_enter_edit (ECellView
*ecv
,
420 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
422 return e_cell_enter_edit (ecp_view
->child_view
, model_col
, view_col
, row
);
426 * ECellView::leave_edit method
429 ecp_leave_edit (ECellView
*ecv
,
433 gpointer edit_context
)
435 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
438 ecp_view
->child_view
, model_col
, view_col
, row
,
443 ecp_print (ECellView
*ecv
,
444 GtkPrintContext
*context
,
451 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
454 ecp_view
->child_view
, context
, model_col
, view_col
, row
,
459 ecp_print_height (ECellView
*ecv
,
460 GtkPrintContext
*context
,
466 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
468 return e_cell_print_height (
469 ecp_view
->child_view
, context
, model_col
,
470 view_col
, row
, width
);
474 ecp_max_width (ECellView
*ecv
,
478 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecv
;
480 return e_cell_max_width (ecp_view
->child_view
, model_col
, view_col
);
484 ecp_get_bg_color (ECellView
*ecell_view
,
487 ECellPopupView
*ecp_view
= (ECellPopupView
*) ecell_view
;
489 return e_cell_get_bg_color (ecp_view
->child_view
, row
);
493 e_cell_popup_get_child (ECellPopup
*ecp
)
495 g_return_val_if_fail (E_IS_CELL_POPUP (ecp
), NULL
);
501 e_cell_popup_set_child (ECellPopup
*ecp
,
504 g_return_if_fail (E_IS_CELL_POPUP (ecp
));
507 g_object_unref (ecp
->child
);
510 g_object_ref (child
);
514 e_cell_popup_do_popup (ECellPopupView
*ecp_view
,
519 ECellPopup
*ecp
= E_CELL_POPUP (ecp_view
->cell_view
.ecell
);
520 gint (*popup_func
) (ECellPopup
*ecp
, GdkEvent
*event
, gint row
, gint view_col
);
522 ecp
->popup_cell_view
= ecp_view
;
524 popup_func
= E_CELL_POPUP_CLASS (G_OBJECT_GET_CLASS (ecp
))->popup
;
526 ecp
->popup_view_col
= view_col
;
527 ecp
->popup_row
= row
;
528 ecp
->popup_model
= ((ECellView
*) ecp_view
)->e_table_model
;
530 return popup_func
? popup_func (ecp
, event
, row
, view_col
) : FALSE
;
533 /* This redraws the popup cell. Only use this if you know popup_view_col and
534 * popup_row are valid. */
536 e_cell_popup_queue_cell_redraw (ECellPopup
*ecp
)
540 g_return_if_fail (ecp
->popup_cell_view
!= NULL
);
542 eti
= E_TABLE_ITEM (ecp
->popup_cell_view
->cell_view
.e_table_item_view
);
544 e_table_item_redraw_range (
545 eti
, ecp
->popup_view_col
, ecp
->popup_row
,
546 ecp
->popup_view_col
, ecp
->popup_row
);
550 e_cell_popup_set_shown (ECellPopup
*ecp
,
553 ecp
->popup_shown
= shown
;
554 e_cell_popup_queue_cell_redraw (ecp
);