evolution-3.6.4/widgets/table/e-cell-popup.c

No issues found

  1 /*
  2  * e-cell-popup.c: Popup cell renderer
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  *
 18  * Authors:
 19  *
 20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 21  *
 22  */
 23 
 24 /*
 25  * ECellPopup - an abstract ECell class used to support popup selections like
 26  * a GtkCombo widget. It contains a child ECell, e.g. an ECellText, but when
 27  * selected it displays an arrow on the right edge which the user can click to
 28  * show a popup. Subclasses implement the popup class function to show the
 29  * popup.
 30  */
 31 
 32 #ifdef HAVE_CONFIG_H
 33 #include <config.h>
 34 #endif
 35 
 36 #include <gdk/gdkkeysyms.h>
 37 
 38 #include "gal-a11y-e-cell-popup.h"
 39 #include "gal-a11y-e-cell-registry.h"
 40 #include "e-util/e-util.h"
 41 
 42 #include "e-cell-popup.h"
 43 #include "e-table-item.h"
 44 #include <gtk/gtk.h>
 45 
 46 #define E_CELL_POPUP_ARROW_WIDTH	16
 47 #define E_CELL_POPUP_ARROW_XPAD		3
 48 #define E_CELL_POPUP_ARROW_YPAD		3
 49 
 50 static void	e_cell_popup_dispose	(GObject	*object);
 51 
 52 static ECellView * ecp_new_view		(ECell		*ecell,
 53 					 ETableModel	*table_model,
 54 					 void		*e_table_item_view);
 55 static void	ecp_kill_view		(ECellView	*ecv);
 56 static void	ecp_realize		(ECellView	*ecv);
 57 static void	ecp_unrealize		(ECellView	*ecv);
 58 static void	ecp_draw		(ECellView	*ecv,
 59 					 cairo_t	*cr,
 60 					 gint		 model_col,
 61 					 gint		 view_col,
 62 					 gint		 row,
 63 					 ECellFlags	 flags,
 64 					 gint		 x1,
 65 					 gint		 y1,
 66 					 gint		 x2,
 67 					 gint		 y2);
 68 static gint	ecp_event		(ECellView	*ecv,
 69 					 GdkEvent	*event,
 70 					 gint		 model_col,
 71 					 gint		 view_col,
 72 					 gint		 row,
 73 					 ECellFlags	 flags,
 74 					 ECellActions	*actions);
 75 static gint	ecp_height		(ECellView	*ecv,
 76 					 gint		 model_col,
 77 					 gint		 view_col,
 78 					 gint		 row);
 79 static gpointer	ecp_enter_edit		(ECellView	*ecv,
 80 					 gint		 model_col,
 81 					 gint		 view_col,
 82 					 gint		 row);
 83 static void	ecp_leave_edit		(ECellView	*ecv,
 84 					 gint		 model_col,
 85 					 gint		 view_col,
 86 					 gint		 row,
 87 					 void		*edit_context);
 88 static void	ecp_print		(ECellView	*ecv,
 89 					 GtkPrintContext *context,
 90 					 gint		 model_col,
 91 					 gint		 view_col,
 92 					 gint		 row,
 93 					 gdouble		 width,
 94 					 gdouble		 height);
 95 static gdouble	ecp_print_height	(ECellView	*ecv,
 96 					 GtkPrintContext *context,
 97 					 gint		 model_col,
 98 					 gint		 view_col,
 99 					 gint		 row,
100 					 gdouble		 width);
101 static gint	ecp_max_width		(ECellView	*ecv,
102 					 gint		 model_col,
103 					 gint		 view_col);
104 static gchar *ecp_get_bg_color (ECellView *ecell_view, gint row);
105 
106 static gint e_cell_popup_do_popup	(ECellPopupView	*ecp_view,
107 					 GdkEvent	*event,
108 					 gint             row,
109 					 gint             model_col);
110 
111 G_DEFINE_TYPE (ECellPopup, e_cell_popup, E_TYPE_CELL)
112 
113 static void
114 e_cell_popup_class_init (ECellPopupClass *class)
115 {
116 	ECellClass *ecc = E_CELL_CLASS (class);
117 
118 	G_OBJECT_CLASS (class)->dispose = e_cell_popup_dispose;
119 
120 	ecc->new_view     = ecp_new_view;
121 	ecc->kill_view    = ecp_kill_view;
122 	ecc->realize      = ecp_realize;
123 	ecc->unrealize    = ecp_unrealize;
124 	ecc->draw         = ecp_draw;
125 	ecc->event        = ecp_event;
126 	ecc->height       = ecp_height;
127 	ecc->enter_edit   = ecp_enter_edit;
128 	ecc->leave_edit   = ecp_leave_edit;
129 	ecc->print        = ecp_print;
130 	ecc->print_height = ecp_print_height;
131 	ecc->max_width	  = ecp_max_width;
132 	ecc->get_bg_color = ecp_get_bg_color;
133 
134 	gal_a11y_e_cell_registry_add_cell_type (
135 		NULL, E_TYPE_CELL_POPUP,
136 		gal_a11y_e_cell_popup_new);
137 }
138 
139 static void
140 e_cell_popup_init (ECellPopup *ecp)
141 {
142 	ecp->popup_shown = FALSE;
143 	ecp->popup_model = NULL;
144 }
145 
146 /**
147  * e_cell_popup_new:
148  *
149  * Creates a new ECellPopup renderer.
150  *
151  * Returns: an ECellPopup object.
152  */
153 ECell *
154 e_cell_popup_new (void)
155 {
156 	return g_object_new (E_TYPE_CELL_POPUP, NULL);
157 }
158 
159 static void
160 e_cell_popup_dispose (GObject *object)
161 {
162 	ECellPopup *ecp = E_CELL_POPUP (object);
163 
164 	if (ecp->child)
165 		g_object_unref (ecp->child);
166 	ecp->child = NULL;
167 
168 	G_OBJECT_CLASS (e_cell_popup_parent_class)->dispose (object);
169 }
170 
171 /*
172  * ECell::new_view method
173  */
174 static ECellView *
175 ecp_new_view (ECell *ecell,
176               ETableModel *table_model,
177               gpointer e_table_item_view)
178 {
179 	ECellPopup *ecp = E_CELL_POPUP (ecell);
180 	ECellPopupView *ecp_view;
181 
182 	/* We must have a child ECell before we create any views. */
183 	g_return_val_if_fail (ecp->child != NULL, NULL);
184 
185 	ecp_view = g_new0 (ECellPopupView, 1);
186 
187 	ecp_view->cell_view.ecell = ecell;
188 	ecp_view->cell_view.e_table_model = table_model;
189 	ecp_view->cell_view.e_table_item_view = e_table_item_view;
190 	ecp_view->cell_view.kill_view_cb = NULL;
191 	ecp_view->cell_view.kill_view_cb_data = NULL;
192 
193 	ecp_view->child_view = e_cell_new_view (
194 		ecp->child, table_model,
195 		e_table_item_view);
196 
197 	return (ECellView *) ecp_view;
198 }
199 
200 /*
201  * ECell::kill_view method
202  */
203 static void
204 ecp_kill_view (ECellView *ecv)
205 {
206 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
207 
208 	if (ecp_view->cell_view.kill_view_cb)
209 		ecp_view->cell_view.kill_view_cb (
210 			ecv, ecp_view->cell_view.kill_view_cb_data);
211 
212 	if (ecp_view->cell_view.kill_view_cb_data)
213 		g_list_free (ecp_view->cell_view.kill_view_cb_data);
214 
215 	if (ecp_view->child_view)
216 		e_cell_kill_view (ecp_view->child_view);
217 
218 	g_free (ecp_view);
219 }
220 
221 /*
222  * ECell::realize method
223  */
224 static void
225 ecp_realize (ECellView *ecv)
226 {
227 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
228 
229 	e_cell_realize (ecp_view->child_view);
230 
231 	if (E_CELL_CLASS (e_cell_popup_parent_class)->realize)
232 		(* E_CELL_CLASS (e_cell_popup_parent_class)->realize) (ecv);
233 }
234 
235 /*
236  * ECell::unrealize method
237  */
238 static void
239 ecp_unrealize (ECellView *ecv)
240 {
241 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
242 
243 	e_cell_unrealize (ecp_view->child_view);
244 
245 	if (E_CELL_CLASS (e_cell_popup_parent_class)->unrealize)
246 		(* E_CELL_CLASS (e_cell_popup_parent_class)->unrealize) (ecv);
247 }
248 
249 /*
250  * ECell::draw method
251  */
252 static void
253 ecp_draw (ECellView *ecv,
254           cairo_t *cr,
255           gint model_col,
256           gint view_col,
257           gint row,
258           ECellFlags flags,
259           gint x1,
260           gint y1,
261           gint x2,
262           gint y2)
263 {
264 	ECellPopup *ecp = E_CELL_POPUP (ecv->ecell);
265 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
266 	GtkWidget *canvas;
267 	GtkShadowType shadow;
268 	GdkRectangle rect;
269 	gboolean show_popup_arrow;
270 
271 	cairo_save (cr);
272 
273 	canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (ecv->e_table_item_view)->canvas);
274 
275 	/* Display the popup arrow if we are the cursor cell, or the popup
276 	 * is shown for this cell. */
277 	show_popup_arrow =
278 		e_table_model_is_cell_editable (
279 			ecv->e_table_model, model_col, row) &&
280 		(flags & E_CELL_CURSOR ||
281 			(ecp->popup_shown && ecp->popup_view_col == view_col
282 			&& ecp->popup_row == row
283 			&& ecp->popup_model == ((ECellView *) ecp_view)->e_table_model));
284 
285 	if (flags & E_CELL_CURSOR)
286 		ecp->popup_arrow_shown = show_popup_arrow;
287 
288 	if (show_popup_arrow) {
289 		GtkStyle *style;
290 
291 		e_cell_draw (
292 			ecp_view->child_view, cr, model_col,
293 			view_col, row, flags,
294 			x1, y1, x2 - E_CELL_POPUP_ARROW_WIDTH, y2);
295 
296 		rect.x = x2 - E_CELL_POPUP_ARROW_WIDTH;
297 		rect.y = y1 + 1;
298 		rect.width = E_CELL_POPUP_ARROW_WIDTH;
299 		rect.height = y2 - y1 - 2;
300 
301 		if (ecp->popup_shown)
302 			shadow = GTK_SHADOW_IN;
303 		else
304 			shadow = GTK_SHADOW_OUT;
305 
306 		style = gtk_widget_get_style (canvas);
307 
308 		gtk_paint_box (
309 			style, cr,
310 			GTK_STATE_NORMAL, shadow,
311 			canvas, "ecellpopup",
312 			rect.x, rect.y, rect.width, rect.height);
313 		gtk_paint_arrow (
314 			style, cr,
315 			GTK_STATE_NORMAL, GTK_SHADOW_NONE,
316 			canvas, NULL,
317 			GTK_ARROW_DOWN, TRUE,
318 			rect.x + E_CELL_POPUP_ARROW_XPAD,
319 			rect.y + E_CELL_POPUP_ARROW_YPAD,
320 			rect.width - E_CELL_POPUP_ARROW_XPAD * 2,
321 			rect.height - E_CELL_POPUP_ARROW_YPAD * 2);
322 	} else {
323 		e_cell_draw (
324 			ecp_view->child_view, cr, model_col,
325 			view_col, row, flags, x1, y1, x2, y2);
326 	}
327 
328 	cairo_restore (cr);
329 }
330 
331 /*
332  * ECell::event method
333  */
334 static gint
335 ecp_event (ECellView *ecv,
336            GdkEvent *event,
337            gint model_col,
338            gint view_col,
339            gint row,
340            ECellFlags flags,
341            ECellActions *actions)
342 {
343 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
344 	ECellPopup *ecp = E_CELL_POPUP (ecp_view->cell_view.ecell);
345 	ETableItem *eti = E_TABLE_ITEM (ecv->e_table_item_view);
346 	gint width;
347 
348 	switch (event->type) {
349 	case GDK_BUTTON_PRESS:
350 		if (e_table_model_is_cell_editable (ecv->e_table_model, model_col, row) &&
351 		    flags & E_CELL_CURSOR
352 		    && ecp->popup_arrow_shown) {
353 			width = e_table_header_col_diff (
354 				eti->header, view_col,
355 				view_col + 1);
356 
357 			/* FIXME: The event coords seem to be relative to the
358 			 * text within the cell, so we have to add 4. */
359 			if (event->button.x + 4 >= width - E_CELL_POPUP_ARROW_WIDTH) {
360 				return e_cell_popup_do_popup (ecp_view, event, row, view_col);
361 			}
362 		}
363 		break;
364 	case GDK_KEY_PRESS:
365 		if (e_table_model_is_cell_editable (ecv->e_table_model, model_col, row) &&
366 		    event->key.state & GDK_MOD1_MASK
367 		    && event->key.keyval == GDK_KEY_Down) {
368 			return e_cell_popup_do_popup (ecp_view, event, row, view_col);
369 		}
370 		break;
371 	default:
372 		break;
373 	}
374 
375 	return e_cell_event (
376 		ecp_view->child_view, event, model_col, view_col,
377 		row, flags, actions);
378 }
379 
380 /*
381  * ECell::height method
382  */
383 static gint
384 ecp_height (ECellView *ecv,
385             gint model_col,
386             gint view_col,
387             gint row)
388 {
389 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
390 
391 	return e_cell_height (ecp_view->child_view, model_col, view_col, row);
392 }
393 
394 /*
395  * ECellView::enter_edit method
396  */
397 static gpointer
398 ecp_enter_edit (ECellView *ecv,
399                 gint model_col,
400                 gint view_col,
401                 gint row)
402 {
403 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
404 
405 	return e_cell_enter_edit (ecp_view->child_view, model_col, view_col, row);
406 }
407 
408 /*
409  * ECellView::leave_edit method
410  */
411 static void
412 ecp_leave_edit (ECellView *ecv,
413                 gint model_col,
414                 gint view_col,
415                 gint row,
416                 gpointer edit_context)
417 {
418 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
419 
420 	e_cell_leave_edit (
421 		ecp_view->child_view, model_col, view_col, row,
422 		edit_context);
423 }
424 
425 static void
426 ecp_print (ECellView *ecv,
427            GtkPrintContext *context,
428            gint model_col,
429            gint view_col,
430            gint row,
431            gdouble width,
432            gdouble height)
433 {
434 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
435 
436 	e_cell_print (
437 		ecp_view->child_view, context, model_col, view_col, row,
438 		width, height);
439 }
440 
441 static gdouble
442 ecp_print_height (ECellView *ecv,
443                   GtkPrintContext *context,
444                   gint model_col,
445                   gint view_col,
446                   gint row,
447                   gdouble width)
448 {
449 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
450 
451 	return e_cell_print_height (
452 		ecp_view->child_view, context, model_col,
453 		view_col, row, width);
454 }
455 
456 static gint
457 ecp_max_width (ECellView *ecv,
458                gint model_col,
459                gint view_col)
460 {
461 	ECellPopupView *ecp_view = (ECellPopupView *) ecv;
462 
463 	return e_cell_max_width (ecp_view->child_view, model_col, view_col);
464 }
465 
466 static gchar *
467 ecp_get_bg_color (ECellView *ecell_view,
468                   gint row)
469 {
470 	ECellPopupView *ecp_view = (ECellPopupView *) ecell_view;
471 
472 	return e_cell_get_bg_color (ecp_view->child_view, row);
473 }
474 
475 ECell *
476 e_cell_popup_get_child (ECellPopup *ecp)
477 {
478 	g_return_val_if_fail (E_IS_CELL_POPUP (ecp), NULL);
479 
480 	return ecp->child;
481 }
482 
483 void
484 e_cell_popup_set_child (ECellPopup *ecp,
485                         ECell *child)
486 {
487 	g_return_if_fail (E_IS_CELL_POPUP (ecp));
488 
489 	if (ecp->child)
490 		g_object_unref (ecp->child);
491 
492 	ecp->child = child;
493 	g_object_ref (child);
494 }
495 
496 static gint
497 e_cell_popup_do_popup (ECellPopupView *ecp_view,
498                        GdkEvent *event,
499                        gint row,
500                        gint view_col)
501 {
502 	ECellPopup *ecp = E_CELL_POPUP (ecp_view->cell_view.ecell);
503 	gint (*popup_func) (ECellPopup *ecp, GdkEvent *event, gint row, gint view_col);
504 
505 	ecp->popup_cell_view = ecp_view;
506 
507 	popup_func = E_CELL_POPUP_CLASS (G_OBJECT_GET_CLASS (ecp))->popup;
508 
509 	ecp->popup_view_col = view_col;
510 	ecp->popup_row = row;
511 	ecp->popup_model = ((ECellView *) ecp_view)->e_table_model;
512 
513 	return popup_func ? popup_func (ecp, event, row, view_col) : FALSE;
514 }
515 
516 /* This redraws the popup cell. Only use this if you know popup_view_col and
517  * popup_row are valid. */
518 void
519 e_cell_popup_queue_cell_redraw (ECellPopup *ecp)
520 {
521 	ETableItem *eti;
522 
523 	eti = E_TABLE_ITEM (ecp->popup_cell_view->cell_view.e_table_item_view);
524 
525 	e_table_item_redraw_range (
526 		eti, ecp->popup_view_col, ecp->popup_row,
527 		ecp->popup_view_col, ecp->popup_row);
528 }
529 
530 void
531 e_cell_popup_set_shown (ECellPopup *ecp,
532                         gboolean shown)
533 {
534 	ecp->popup_shown = shown;
535 	e_cell_popup_queue_cell_redraw (ecp);
536 }