1 /*
2 * Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
3 *
4 * This library 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.1 of the License, or (at your option) any later version.
8 *
9 * This library 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 this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 /**
20 * SECTION:gtk-champlain-embed
21 * @short_description: A Gtk+ Widget that embeds a #ChamplainView
22 *
23 * Since #ChamplainView is a #ClutterActor, you cannot embed it directly
24 * into a Gtk+ application. This widget solves this problem. It creates
25 * the #ChamplainView for you, you can get it with
26 * #gtk_champlain_embed_get_view.
27 */
28 #include "config.h"
29
30 #include <champlain/champlain.h>
31
32 #include <gtk/gtk.h>
33 #include <clutter/clutter.h>
34 #include <clutter-gtk/clutter-gtk.h>
35
36 #include "gtk-champlain-embed.h"
37
38 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION <= 12)
39 #define gtk_widget_get_window(widget) ((widget)->window)
40 #endif
41
42 enum
43 {
44 /* normal signals */
45 LAST_SIGNAL
46 };
47
48 enum
49 {
50 PROP_0,
51 PROP_VIEW
52 };
53
54 /* static guint gtk_champlain_embed_embed_signals[LAST_SIGNAL] = { 0, }; */
55
56 struct _GtkChamplainEmbedPrivate
57 {
58 GtkWidget *clutter_embed;
59 ChamplainView *view;
60
61 GdkCursor *cursor_hand_open;
62 GdkCursor *cursor_hand_closed;
63
64 guint width;
65 guint height;
66 };
67
68
69 static void gtk_champlain_embed_get_property (GObject *object,
70 guint prop_id,
71 GValue *value,
72 GParamSpec *pspec);
73 static void gtk_champlain_embed_set_property (GObject *object,
74 guint prop_id,
75 const GValue *value,
76 GParamSpec *pspec);
77 static void gtk_champlain_embed_finalize (GObject *object);
78 static void gtk_champlain_embed_dispose (GObject *object);
79 static void gtk_champlain_embed_class_init (GtkChamplainEmbedClass *klass);
80 static void gtk_champlain_embed_init (GtkChamplainEmbed *view);
81 static void view_size_allocated_cb (GtkWidget *widget,
82 GtkAllocation *allocation,
83 GtkChamplainEmbed *view);
84 static gboolean mouse_button_cb (GtkWidget *widget,
85 GdkEventButton *event,
86 GtkChamplainEmbed *view);
87 static void view_size_allocated_cb (GtkWidget *widget,
88 GtkAllocation *allocation,
89 GtkChamplainEmbed *view);
90 static void view_realize_cb (GtkWidget *widget,
91 GtkChamplainEmbed *view);
92 static gboolean embed_focus_cb (GtkChamplainEmbed *embed,
93 GdkEvent *event);
94 static gboolean stage_key_press_cb (ClutterActor *actor,
95 ClutterEvent *event,
96 GtkChamplainEmbed *embed);
97
G_DEFINE_TYPE_WITH_PRIVATE(GtkChamplainEmbed,gtk_champlain_embed,GTK_TYPE_ALIGNMENT)98 G_DEFINE_TYPE_WITH_PRIVATE (GtkChamplainEmbed, gtk_champlain_embed, GTK_TYPE_ALIGNMENT)
99
100 static void
101 gtk_champlain_embed_get_property (GObject *object,
102 guint prop_id,
103 GValue *value,
104 GParamSpec *pspec)
105 {
106 GtkChamplainEmbed *embed = GTK_CHAMPLAIN_EMBED (object);
107 GtkChamplainEmbedPrivate *priv = embed->priv;
108
109 switch (prop_id)
110 {
111 case PROP_VIEW:
112 g_value_set_object (value, priv->view);
113 break;
114
115 default:
116 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117 }
118 }
119
120
121 static void
gtk_champlain_embed_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)122 gtk_champlain_embed_set_property (GObject *object,
123 guint prop_id,
124 const GValue *value,
125 GParamSpec *pspec)
126 {
127 switch (prop_id)
128 {
129 default:
130 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 }
132 }
133
134
135 static void
gtk_champlain_embed_dispose(GObject * object)136 gtk_champlain_embed_dispose (GObject *object)
137 {
138 GtkChamplainEmbed *embed = GTK_CHAMPLAIN_EMBED (object);
139 GtkChamplainEmbedPrivate *priv = embed->priv;
140
141 if (priv->cursor_hand_open != NULL)
142 {
143 #if (GTK_MAJOR_VERSION == 2)
144 gdk_cursor_unref (priv->cursor_hand_open);
145 #else
146 g_object_unref (priv->cursor_hand_open);
147 #endif
148 priv->cursor_hand_open = NULL;
149 }
150
151 if (priv->cursor_hand_closed != NULL)
152 {
153 #if (GTK_MAJOR_VERSION == 2)
154 gdk_cursor_unref (priv->cursor_hand_closed);
155 #else
156 g_object_unref (priv->cursor_hand_closed);
157 #endif
158 priv->cursor_hand_closed = NULL;
159 }
160
161 G_OBJECT_CLASS (gtk_champlain_embed_parent_class)->dispose (object);
162 }
163
164
165 static void
gtk_champlain_embed_finalize(GObject * object)166 gtk_champlain_embed_finalize (GObject *object)
167 {
168 G_OBJECT_CLASS (gtk_champlain_embed_parent_class)->finalize (object);
169 }
170
171
172 static void
gtk_champlain_embed_class_init(GtkChamplainEmbedClass * klass)173 gtk_champlain_embed_class_init (GtkChamplainEmbedClass *klass)
174 {
175 GObjectClass *object_class = G_OBJECT_CLASS (klass);
176 object_class->finalize = gtk_champlain_embed_finalize;
177 object_class->dispose = gtk_champlain_embed_dispose;
178 object_class->get_property = gtk_champlain_embed_get_property;
179 object_class->set_property = gtk_champlain_embed_set_property;
180
181 /**
182 * GtkChamplainEmbed:champlain-view:
183 *
184 * The #ChamplainView to embed in the Gtk+ widget.
185 *
186 * Since: 0.4
187 */
188 g_object_class_install_property (object_class,
189 PROP_VIEW,
190 g_param_spec_object ("champlain-view",
191 "Champlain view",
192 "The ChamplainView to embed into the Gtk+ widget",
193 CHAMPLAIN_TYPE_VIEW,
194 G_PARAM_READABLE));
195 }
196
197
198 static void
set_view(GtkChamplainEmbed * embed,ChamplainView * view)199 set_view (GtkChamplainEmbed *embed,
200 ChamplainView *view)
201 {
202 GtkChamplainEmbedPrivate *priv = embed->priv;
203 ClutterActor *stage;
204
205 stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->clutter_embed));
206
207 if (priv->view != NULL)
208 clutter_actor_remove_child (stage, CLUTTER_ACTOR (priv->view));
209
210 priv->view = view;
211 clutter_actor_set_size (CLUTTER_ACTOR (priv->view), priv->width, priv->height);
212
213 clutter_actor_add_child (stage, CLUTTER_ACTOR (priv->view));
214 }
215
216
217 static void
gtk_champlain_embed_init(GtkChamplainEmbed * embed)218 gtk_champlain_embed_init (GtkChamplainEmbed *embed)
219 {
220 GtkChamplainEmbedPrivate *priv = gtk_champlain_embed_get_instance_private (embed);
221 ClutterActor *stage;
222 GdkDisplay *display;
223
224 embed->priv = priv;
225
226 priv->clutter_embed = gtk_clutter_embed_new ();
227
228 g_signal_connect (priv->clutter_embed,
229 "size-allocate",
230 G_CALLBACK (view_size_allocated_cb),
231 embed);
232 g_signal_connect (priv->clutter_embed,
233 "realize",
234 G_CALLBACK (view_realize_cb),
235 embed);
236 g_signal_connect (priv->clutter_embed,
237 "button-press-event",
238 G_CALLBACK (mouse_button_cb),
239 embed);
240 g_signal_connect (priv->clutter_embed,
241 "button-release-event",
242 G_CALLBACK (mouse_button_cb),
243 embed);
244 /* Setup cursors */
245 display = gdk_display_get_default ();
246 priv->cursor_hand_open = gdk_cursor_new_for_display (display, GDK_HAND1);
247 priv->cursor_hand_closed = gdk_cursor_new_for_display (display, GDK_FLEUR);
248
249 priv->view = NULL;
250 set_view (embed, CHAMPLAIN_VIEW (champlain_view_new ()));
251
252 /* Setup focus/key-press events */
253 g_signal_connect (embed, "focus-in-event",
254 G_CALLBACK (embed_focus_cb),
255 NULL);
256 stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->clutter_embed));
257 g_signal_connect (stage, "key-press-event",
258 G_CALLBACK (stage_key_press_cb),
259 embed);
260 gtk_widget_set_can_focus (GTK_WIDGET (embed), TRUE);
261
262 gtk_container_add (GTK_CONTAINER (embed), priv->clutter_embed);
263 }
264
265
266 #if (GTK_MAJOR_VERSION == 2)
267 static void
gdk_to_clutter_color(GdkColor * gdk_color,ClutterColor * color)268 gdk_to_clutter_color (GdkColor *gdk_color,
269 ClutterColor *color)
270 {
271 color->red = CLAMP ((gdk_color->red / 65535.0) * 255, 0, 255);
272 color->green = CLAMP ((gdk_color->green / 65535.0) * 255, 0, 255);
273 color->blue = CLAMP ((gdk_color->blue / 65535.0) * 255, 0, 255);
274 color->alpha = 255;
275 }
276 #else
277 static void
gdk_rgba_to_clutter_color(GdkRGBA * gdk_rgba_color,ClutterColor * color)278 gdk_rgba_to_clutter_color (GdkRGBA *gdk_rgba_color,
279 ClutterColor *color)
280 {
281 color->red = CLAMP (gdk_rgba_color->red * 255, 0, 255);
282 color->green = CLAMP (gdk_rgba_color->green * 255, 0, 255);
283 color->blue = CLAMP (gdk_rgba_color->blue * 255, 0, 255);
284 color->alpha = CLAMP (gdk_rgba_color->alpha * 255, 0, 255);
285 }
286
287 static void
get_background_color(GtkStyleContext * context,GtkStateFlags state,GdkRGBA * color)288 get_background_color (GtkStyleContext *context,
289 GtkStateFlags state,
290 GdkRGBA *color)
291 {
292 GdkRGBA *c;
293
294 gtk_style_context_get (context, state, "background-color", &c, NULL);
295 *color = *c;
296 gdk_rgba_free (c);
297 }
298 #endif
299
300
301 static void
view_realize_cb(GtkWidget * widget,GtkChamplainEmbed * view)302 view_realize_cb (GtkWidget *widget,
303 GtkChamplainEmbed *view)
304 {
305 ClutterColor color = { 0, 0, 0, };
306 GtkChamplainEmbedPrivate *priv = view->priv;
307
308 #if (GTK_MAJOR_VERSION == 2)
309 GtkStyle *style;
310
311 /* Set selection color */
312 style = gtk_widget_get_style (widget);
313
314 gdk_to_clutter_color (&style->text[GTK_STATE_SELECTED], &color);
315 if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0)
316 {
317 color.red = 255;
318 color.green = 255;
319 color.blue = 255;
320 }
321 champlain_marker_set_selection_text_color (&color);
322
323 gdk_to_clutter_color (&style->bg[GTK_STATE_SELECTED], &color);
324 if (color.alpha == 0)
325 color.alpha = 255;
326 if (color.red == 0 && color.green == 0 && color.blue == 0)
327 {
328 color.red = 75;
329 color.green = 105;
330 color.blue = 131;
331 }
332 champlain_marker_set_selection_color (&color);
333 #else
334 GtkStyleContext *style;
335 GdkRGBA gdk_rgba_color;
336
337 /* Set selection color */
338 style = gtk_widget_get_style_context (widget);
339 gtk_style_context_save (style);
340 gtk_style_context_set_state (style, GTK_STATE_FLAG_SELECTED);
341
342 gtk_style_context_get_color (style, gtk_style_context_get_state (style),
343 &gdk_rgba_color);
344 gdk_rgba_to_clutter_color (&gdk_rgba_color, &color);
345 if (color.alpha == 0 && color.red == 0 && color.green == 0 && color.blue == 0)
346 {
347 color.red = 255;
348 color.green = 255;
349 color.blue = 255;
350 }
351 champlain_marker_set_selection_text_color (&color);
352
353 get_background_color (style, gtk_style_context_get_state (style),
354 &gdk_rgba_color);
355 gdk_rgba_to_clutter_color (&gdk_rgba_color, &color);
356 if (color.alpha == 0)
357 color.alpha = 255;
358 if (color.red == 0 && color.green == 0 && color.blue == 0)
359 {
360 color.red = 75;
361 color.green = 105;
362 color.blue = 131;
363 }
364 champlain_marker_set_selection_color (&color);
365
366 gtk_style_context_restore (style);
367 #endif
368
369 /* Setup mouse cursor to a hand */
370 gdk_window_set_cursor (gtk_widget_get_window (priv->clutter_embed), priv->cursor_hand_open);
371 }
372
373
374 static void
view_size_allocated_cb(GtkWidget * widget,GtkAllocation * allocation,GtkChamplainEmbed * view)375 view_size_allocated_cb (GtkWidget *widget,
376 GtkAllocation *allocation,
377 GtkChamplainEmbed *view)
378 {
379 GtkChamplainEmbedPrivate *priv = view->priv;
380
381 if (priv->view != NULL)
382 clutter_actor_set_size (CLUTTER_ACTOR (priv->view), allocation->width, allocation->height);
383
384 priv->width = allocation->width;
385 priv->height = allocation->height;
386 }
387
388
389 static gboolean
mouse_button_cb(GtkWidget * widget,GdkEventButton * event,GtkChamplainEmbed * view)390 mouse_button_cb (GtkWidget *widget,
391 GdkEventButton *event,
392 GtkChamplainEmbed *view)
393 {
394 GtkChamplainEmbedPrivate *priv = view->priv;
395
396 if (event->type == GDK_BUTTON_PRESS)
397 gdk_window_set_cursor (gtk_widget_get_window (priv->clutter_embed),
398 priv->cursor_hand_closed);
399 else
400 gdk_window_set_cursor (gtk_widget_get_window (priv->clutter_embed),
401 priv->cursor_hand_open);
402
403 return FALSE;
404 }
405
406 static gboolean
embed_focus_cb(GtkChamplainEmbed * embed,GdkEvent * event)407 embed_focus_cb (GtkChamplainEmbed *embed,
408 GdkEvent *event)
409 {
410 GtkChamplainEmbedPrivate *priv = embed->priv;
411
412 gtk_widget_grab_focus (priv->clutter_embed);
413 return TRUE;
414 }
415
416 static gboolean
stage_key_press_cb(ClutterActor * actor,ClutterEvent * event,GtkChamplainEmbed * embed)417 stage_key_press_cb (ClutterActor *actor,
418 ClutterEvent *event,
419 GtkChamplainEmbed *embed)
420 {
421 ChamplainView *view = gtk_champlain_embed_get_view (embed);
422
423 clutter_actor_event (CLUTTER_ACTOR (view), event, FALSE);
424 return TRUE;
425 }
426
427 /**
428 * gtk_champlain_embed_new:
429 *
430 * Creates an instance of #GtkChamplainEmbed.
431 *
432 * Return value: a new #GtkChamplainEmbed ready to be used as a #GtkWidget.
433 *
434 * Since: 0.4
435 */
436 GtkWidget *
gtk_champlain_embed_new()437 gtk_champlain_embed_new ()
438 {
439 return g_object_new (GTK_CHAMPLAIN_TYPE_EMBED, NULL);
440 }
441
442
443 /**
444 * gtk_champlain_embed_get_view:
445 * @embed: a #ChamplainView, the map view to embed
446 *
447 * Gets a #ChamplainView from the #GtkChamplainEmbed object.
448 *
449 * Return value: (transfer none): a #ChamplainView ready to be used
450 *
451 * Since: 0.4
452 */
453 ChamplainView *
gtk_champlain_embed_get_view(GtkChamplainEmbed * embed)454 gtk_champlain_embed_get_view (GtkChamplainEmbed *embed)
455 {
456 g_return_val_if_fail (GTK_CHAMPLAIN_IS_EMBED (embed), NULL);
457
458 GtkChamplainEmbedPrivate *priv = embed->priv;
459 return priv->view;
460 }
461