1 /*
2 * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2010 David King <davidk@openismus.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include <libgda/libgda.h>
21 #include "browser-canvas.h"
22 #include "browser-canvas-text.h"
23
24 static void browser_canvas_text_class_init (BrowserCanvasTextClass * class);
25 static void browser_canvas_text_init (BrowserCanvasText *text);
26 static void browser_canvas_text_dispose (GObject * object);
27 static void browser_canvas_text_finalize (GObject * object);
28
29 static void browser_canvas_text_set_property (GObject *object,
30 guint param_id,
31 const GValue *value,
32 GParamSpec *pspec);
33 static void browser_canvas_text_get_property (GObject *object,
34 guint param_id,
35 GValue *value,
36 GParamSpec *pspec);
37
38 static gboolean enter_notify_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventCrossing *event, BrowserCanvasText *ct);
39 static gboolean leave_notify_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventCrossing *event, BrowserCanvasText *ct);
40
41 enum
42 {
43 PROP_0,
44 PROP_TEXT,
45 PROP_WIDTH,
46 PROP_HEIGHT,
47 PROP_HIGHLIGHT_COLOR,
48 PROP_UNDERLINE,
49 PROP_BOLD
50 };
51
52 struct _BrowserCanvasTextPrivate
53 {
54 gchar *text;
55
56 /* properties */
57 gboolean underline;
58 gboolean bold;
59 gchar *highlight_color;
60
61 /* UI building information */
62 GooCanvasItem *bg_item;
63 GooCanvasItem *text_item;
64
65 /* animation */
66 guint anim_id;
67 guint current_anim_rgba;
68 guint end_anim_rgba;
69
70 };
71
72 /* get a pointer to the parents to be able to call their destructor */
73 static GObjectClass *text_parent_class = NULL;
74
75 GType
browser_canvas_text_get_type(void)76 browser_canvas_text_get_type (void)
77 {
78 static GType type = 0;
79
80 if (G_UNLIKELY (type == 0)) {
81 static const GTypeInfo info = {
82 sizeof (BrowserCanvasTextClass),
83 (GBaseInitFunc) NULL,
84 (GBaseFinalizeFunc) NULL,
85 (GClassInitFunc) browser_canvas_text_class_init,
86 NULL,
87 NULL,
88 sizeof (BrowserCanvasText),
89 0,
90 (GInstanceInitFunc) browser_canvas_text_init,
91 0
92 };
93
94 type = g_type_register_static (TYPE_BROWSER_CANVAS_ITEM, "BrowserCanvasText", &info, 0);
95 }
96
97 return type;
98 }
99
100 static void
browser_canvas_text_class_init(BrowserCanvasTextClass * class)101 browser_canvas_text_class_init (BrowserCanvasTextClass * class)
102 {
103 GObjectClass *object_class = G_OBJECT_CLASS (class);
104
105 text_parent_class = g_type_class_peek_parent (class);
106
107 object_class->dispose = browser_canvas_text_dispose;
108 object_class->finalize = browser_canvas_text_finalize;
109
110 /* Properties */
111 object_class->set_property = browser_canvas_text_set_property;
112 object_class->get_property = browser_canvas_text_get_property;
113
114 g_object_class_install_property
115 (object_class, PROP_WIDTH,
116 g_param_spec_double ("width", NULL, NULL, 0., G_MAXDOUBLE, 0., G_PARAM_WRITABLE));
117
118 g_object_class_install_property
119 (object_class, PROP_HEIGHT,
120 g_param_spec_double ("height", NULL, NULL, 0., G_MAXDOUBLE, 0., G_PARAM_WRITABLE));
121
122 g_object_class_install_property
123 (object_class, PROP_TEXT,
124 g_param_spec_string ("text", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
125
126 g_object_class_install_property
127 (object_class, PROP_HIGHLIGHT_COLOR,
128 g_param_spec_string ("highlight_color", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
129
130 g_object_class_install_property
131 (object_class, PROP_UNDERLINE,
132 g_param_spec_boolean ("text_underline", NULL, NULL, FALSE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
133
134 g_object_class_install_property
135 (object_class, PROP_BOLD,
136 g_param_spec_boolean ("text_bold", NULL, NULL, FALSE, (G_PARAM_READABLE | G_PARAM_WRITABLE)));
137 }
138
139 static void
browser_canvas_text_init(BrowserCanvasText * text)140 browser_canvas_text_init (BrowserCanvasText *text)
141 {
142 text->priv = g_new0 (BrowserCanvasTextPrivate, 1);
143 text->priv->text = NULL;
144 text->priv->highlight_color = g_strdup (BROWSER_CANVAS_ENTITY_COLOR);
145
146 g_signal_connect (G_OBJECT (text), "enter-notify-event",
147 G_CALLBACK (enter_notify_cb), text);
148 g_signal_connect (G_OBJECT (text), "leave-notify-event",
149 G_CALLBACK (leave_notify_cb), text);
150 }
151
152 static void
browser_canvas_text_dispose(GObject * object)153 browser_canvas_text_dispose (GObject *object)
154 {
155 BrowserCanvasText *ct;
156 g_return_if_fail (object != NULL);
157 g_return_if_fail (IS_BROWSER_CANVAS_TEXT (object));
158
159 ct = BROWSER_CANVAS_TEXT (object);
160
161 /* animation */
162 if (ct->priv->anim_id) {
163 g_source_remove (ct->priv->anim_id);
164 ct->priv->anim_id = 0;
165 }
166
167 /* for the parent class */
168 text_parent_class->dispose (object);
169 }
170
171
172 static void
browser_canvas_text_finalize(GObject * object)173 browser_canvas_text_finalize (GObject *object)
174 {
175 BrowserCanvasText *ct;
176 g_return_if_fail (object != NULL);
177 g_return_if_fail (IS_BROWSER_CANVAS_TEXT (object));
178
179 ct = BROWSER_CANVAS_TEXT (object);
180 if (ct->priv) {
181 g_free (ct->priv->text);
182
183 if (ct->priv->highlight_color)
184 g_free (ct->priv->highlight_color);
185
186 g_free (ct->priv);
187 ct->priv = NULL;
188 }
189
190 /* for the parent class */
191 text_parent_class->finalize (object);
192 }
193
194 static void clean_items (BrowserCanvasText *ct);
195 static void create_items (BrowserCanvasText *ct);
196
197 static void
adjust_text_pango_attributes(BrowserCanvasText * ct)198 adjust_text_pango_attributes (BrowserCanvasText *ct)
199 {
200 if (! ct->priv->text_item)
201 return;
202
203 if (ct->priv->bold || ct->priv->underline) {
204 gchar *str;
205 if (ct->priv->bold) {
206 if (ct->priv->underline)
207 str = g_strdup_printf ("<b><u>%s</u></b>", ct->priv->text);
208 else
209 str = g_strdup_printf ("<b>%s</b>", ct->priv->text);
210 }
211 else
212 str = g_strdup_printf ("<u>%s</u>", ct->priv->text);
213 g_object_set (G_OBJECT (ct->priv->text_item),
214 "text", str,
215 "use-markup", TRUE, NULL);
216 g_free (str);
217 }
218 else
219 g_object_set (G_OBJECT (ct->priv->text_item),
220 "text", ct->priv->text,
221 "use-markup", FALSE, NULL);
222 }
223
224 static void
browser_canvas_text_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)225 browser_canvas_text_set_property (GObject *object,
226 guint param_id,
227 const GValue *value,
228 GParamSpec *pspec)
229 {
230 BrowserCanvasText *ct = NULL;
231 const gchar *cstr = NULL;
232 gchar *str;
233 gdouble size = 0;
234 gboolean bool = FALSE;
235
236 ct = BROWSER_CANVAS_TEXT (object);
237
238 switch (param_id) {
239 case PROP_TEXT:
240 g_free (ct->priv->text);
241 ct->priv->text = NULL;
242 clean_items (ct);
243 ct->priv->text = g_strdup (g_value_get_string (value));
244 create_items (ct);
245 break;
246 case PROP_WIDTH:
247 size = g_value_get_double (value);
248 if (ct->priv->bg_item)
249 g_object_set (G_OBJECT (ct->priv->bg_item),
250 "width", size,
251 NULL);
252 break;
253 case PROP_HEIGHT:
254 size = g_value_get_double (value);
255 if (ct->priv->bg_item)
256 g_object_set (G_OBJECT (ct->priv->bg_item),
257 "height", size,
258 NULL);
259 break;
260 case PROP_HIGHLIGHT_COLOR:
261 cstr = g_value_get_string (value);
262 if (ct->priv->highlight_color) {
263 g_free (ct->priv->highlight_color);
264 ct->priv->highlight_color = NULL;
265 }
266 if (cstr)
267 ct->priv->highlight_color = g_strdup (cstr);
268 else
269 ct->priv->highlight_color = g_strdup (BROWSER_CANVAS_ENTITY_COLOR);
270 break;
271 case PROP_UNDERLINE:
272 bool = g_value_get_boolean (value);
273 ct->priv->underline = bool;
274 adjust_text_pango_attributes (ct);
275 if (ct->priv->text_item) {
276 if (bool) {
277 str = g_strdup_printf ("<u>%s</u>", ct->priv->text);
278 g_object_set (G_OBJECT (ct->priv->text_item),
279 "text", str,
280 "use-markup", TRUE, NULL);
281 g_free (str);
282 }
283 else
284 g_object_set (G_OBJECT (ct->priv->text_item),
285 "text", ct->priv->text,
286 "use-markup", FALSE, NULL);
287 }
288 case PROP_BOLD:
289 bool = g_value_get_boolean (value);
290 ct->priv->bold = bool;
291 adjust_text_pango_attributes (ct);
292 break;
293 default:
294 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
295 break;
296 }
297 }
298
299 static void
browser_canvas_text_get_property(GObject * object,guint param_id,G_GNUC_UNUSED GValue * value,GParamSpec * pspec)300 browser_canvas_text_get_property (GObject *object,
301 guint param_id,
302 G_GNUC_UNUSED GValue *value,
303 GParamSpec *pspec)
304 {
305 switch (param_id) {
306 default:
307 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
308 break;
309 }
310 }
311
312 /*
313 * destroy any existing GooCanvasItem obejcts
314 */
315 static void
clean_items(BrowserCanvasText * ct)316 clean_items (BrowserCanvasText *ct)
317 {
318 if (ct->priv->bg_item) {
319 goo_canvas_item_remove (GOO_CANVAS_ITEM (ct->priv->bg_item));
320 ct->priv->bg_item = NULL;
321 }
322 if (ct->priv->text_item) {
323 goo_canvas_item_remove (GOO_CANVAS_ITEM (ct->priv->text_item));
324 ct->priv->text_item = NULL;
325 }
326 }
327
328 /*
329 * create new GooCanvasItem objects
330 */
331 static void
create_items(BrowserCanvasText * ct)332 create_items (BrowserCanvasText *ct)
333 {
334 GooCanvasItem *item, *text;
335 GooCanvasBounds bounds;
336
337 g_object_set (G_OBJECT (ct),
338 "allow_move", FALSE,
339 NULL);
340
341 /* text: text's name */
342 text = goo_canvas_text_new (GOO_CANVAS_ITEM (ct), ct->priv->text,
343 0., 0.,
344 -1, GOO_CANVAS_ANCHOR_NORTH_WEST,
345 "fill_color", "black",
346 "font", BROWSER_CANVAS_FONT,
347 "alignment", PANGO_ALIGN_RIGHT,
348 NULL);
349 ct->priv->text_item = text;
350
351 /* UI metrics */
352 goo_canvas_item_get_bounds (text, &bounds);
353
354 /* background */
355 item = goo_canvas_rect_new (GOO_CANVAS_ITEM (ct),
356 0., 0.,
357 bounds.x2 - bounds.x1,
358 bounds.y2 - bounds.y1,
359 "fill_color", BROWSER_CANVAS_OBJ_BG_COLOR,
360 "radius-x", 2.,
361 "radius-y", 2.,
362 "stroke-pattern", NULL,
363 NULL);
364
365 ct->priv->bg_item = item;
366 goo_canvas_item_lower (item, NULL);
367
368 adjust_text_pango_attributes (ct);
369 }
370
371 static gboolean
enter_notify_cb(G_GNUC_UNUSED GooCanvasItem * item,G_GNUC_UNUSED GooCanvasItem * target_item,G_GNUC_UNUSED GdkEventCrossing * event,BrowserCanvasText * ct)372 enter_notify_cb (G_GNUC_UNUSED GooCanvasItem *item, G_GNUC_UNUSED GooCanvasItem *target_item,
373 G_GNUC_UNUSED GdkEventCrossing *event, BrowserCanvasText *ct)
374 {
375 browser_canvas_text_set_highlight (ct, TRUE);
376 return FALSE;
377 }
378
379 static gboolean
leave_notify_cb(G_GNUC_UNUSED GooCanvasItem * item,G_GNUC_UNUSED GooCanvasItem * target_item,G_GNUC_UNUSED GdkEventCrossing * event,BrowserCanvasText * ct)380 leave_notify_cb (G_GNUC_UNUSED GooCanvasItem *item, G_GNUC_UNUSED GooCanvasItem *target_item,
381 G_GNUC_UNUSED GdkEventCrossing *event, BrowserCanvasText *ct)
382 {
383 browser_canvas_text_set_highlight (ct, FALSE);
384 return FALSE;
385 }
386
387 static guint
compute_step_value(guint current,guint end)388 compute_step_value (guint current, guint end)
389 {
390 const guint STEP = 15;
391 if (current < end)
392 return current + MIN (STEP, (end - current));
393 else if (current > end)
394 return current - MIN (STEP, (current - end));
395 else
396 return current;
397 }
398
399 static gboolean
anim_cb(BrowserCanvasText * ct)400 anim_cb (BrowserCanvasText *ct)
401 {
402 guint current, end, value;
403 guint rgba = 0;
404
405 /* red */
406 current = (ct->priv->current_anim_rgba >> 24) & 0xFF;
407 end = (ct->priv->end_anim_rgba >> 24) & 0xFF;
408 value = compute_step_value (current, end) << 24;
409 rgba += value;
410
411 /* green */
412 current = (ct->priv->current_anim_rgba >> 16) & 0xFF;
413 end = (ct->priv->end_anim_rgba >> 16) & 0xFF;
414 value = compute_step_value (current, end) << 16;
415 rgba += value;
416
417 /* blue */
418 current = (ct->priv->current_anim_rgba >> 8) & 0xFF;
419 end = (ct->priv->end_anim_rgba >> 8) & 0xFF;
420 value = compute_step_value (current, end) << 8;
421 rgba += value;
422
423 /* alpha */
424 current = ct->priv->current_anim_rgba & 0xFF;
425 end = ct->priv->end_anim_rgba & 0xFF;
426 value = compute_step_value (current, end);
427 rgba += value;
428
429 if (rgba == ct->priv->end_anim_rgba) {
430 ct->priv->anim_id = 0;
431 return FALSE;
432 }
433 else {
434 g_object_set (G_OBJECT (ct->priv->bg_item), "fill_color_rgba", rgba, NULL);
435 ct->priv->current_anim_rgba = rgba;
436 return TRUE;
437 }
438 }
439
440 /**
441 * browser_canvas_text_set_highlight
442 * @ct: a #BrowserCanvasText object
443 * @highlight:
444 *
445 * Turns ON or OFF the highlighting of @ct
446 */
447 void
browser_canvas_text_set_highlight(BrowserCanvasText * ct,gboolean highlight)448 browser_canvas_text_set_highlight (BrowserCanvasText *ct, gboolean highlight)
449 {
450 gchar *str_color;
451 GdkColor gdk_color;
452
453 g_return_if_fail (ct && IS_BROWSER_CANVAS_TEXT (ct));
454 g_return_if_fail (ct->priv);
455
456 if (! ct->priv->bg_item)
457 return;
458
459 if (ct->priv->anim_id) {
460 g_source_remove (ct->priv->anim_id);
461 ct->priv->anim_id = 0;
462 }
463
464 str_color = highlight ? ct->priv->highlight_color : BROWSER_CANVAS_OBJ_BG_COLOR;
465 if (gdk_color_parse (str_color, &gdk_color)) {
466 guint col;
467
468 col = ((guint) (gdk_color.red * 255. / 65535.0));
469 ct->priv->end_anim_rgba = col << 24;
470 col = ((guint) (gdk_color.green * 255. / 65535.0));
471 ct->priv->end_anim_rgba += col << 16;
472 col = ((guint) (gdk_color.blue * 255. / 65535.0));
473 ct->priv->end_anim_rgba += col << 8;
474 if (!ct->priv->current_anim_rgba)
475 ct->priv->current_anim_rgba = ct->priv->end_anim_rgba;
476
477 if (highlight)
478 ct->priv->end_anim_rgba += 255;
479 else
480 ct->priv->end_anim_rgba += 50;
481
482 ct->priv->anim_id = g_timeout_add (10, (GSourceFunc) anim_cb, ct);
483 }
484 else
485 g_object_set (G_OBJECT (ct->priv->bg_item), "fill_color", str_color, NULL);
486 }
487
488 /**
489 * browser_canvas_text_new
490 * @parent: the parent item, or NULL.
491 * @txt: text to display
492 * @x: the x coordinate of the text.
493 * @y: the y coordinate of the text.
494 * @...: optional pairs of property names and values, and a terminating NULL.
495 *
496 * Creates a new canvas item to display the @txt message
497 *
498 * Returns: a new #GooCanvasItem object
499 */
500 GooCanvasItem *
browser_canvas_text_new(GooCanvasItem * parent,const gchar * txt,gdouble x,gdouble y,...)501 browser_canvas_text_new (GooCanvasItem *parent,
502 const gchar *txt,
503 gdouble x,
504 gdouble y,
505 ...)
506 {
507 GooCanvasItem *item;
508 const char *first_property;
509 va_list var_args;
510
511 item = g_object_new (TYPE_BROWSER_CANVAS_TEXT, NULL);
512
513 if (parent) {
514 goo_canvas_item_add_child (parent, item, -1);
515 g_object_unref (item);
516 }
517
518 va_start (var_args, y);
519 first_property = va_arg (var_args, char*);
520 if (first_property)
521 g_object_set_valist ((GObject*) item, first_property, var_args);
522 va_end (var_args);
523
524 g_object_set (item, "text", txt, NULL);
525 goo_canvas_item_translate (item, x, y);
526
527 return item;
528 }
529