Fix view destruction upon shutdown.
[libchamplain:potyl-perl.git] / champlain-gtk / gtk-champlain-embed.c
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 #define GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), GTK_TYPE_CHAMPLAIN_EMBED, GtkChamplainEmbedPrivate))
57
58 struct _GtkChamplainEmbedPrivate
59 {
60   GtkWidget *clutter_embed;
61   ChamplainView *view;
62
63   GdkCursor *cursor_hand_open;
64   GdkCursor *cursor_hand_closed;
65
66   guint width;
67   guint height;
68 };
69
70
71 static void gtk_champlain_embed_get_property (GObject *object, guint prop_id,
72     GValue *value, GParamSpec *pspec);
73 static void gtk_champlain_embed_set_property (GObject *object, guint prop_id,
74     const GValue *value, GParamSpec *pspec);
75 static void gtk_champlain_embed_finalize (GObject *object);
76 static void gtk_champlain_embed_dispose (GObject *object);
77 static void gtk_champlain_embed_class_init (GtkChamplainEmbedClass *klass);
78 static void gtk_champlain_embed_init (GtkChamplainEmbed *view);
79 static void view_size_allocated_cb (GtkWidget *widget,
80     GtkAllocation *allocation, GtkChamplainEmbed *view);
81 static gboolean mouse_button_cb (GtkWidget *widget, GdkEventButton *event,
82     GtkChamplainEmbed *view);
83 static void view_size_allocated_cb (GtkWidget *widget,
84     GtkAllocation *allocation, GtkChamplainEmbed *view);
85 static void view_realize_cb (GtkWidget *widget,
86     GtkChamplainEmbed *view);
87
88 G_DEFINE_TYPE (GtkChamplainEmbed, gtk_champlain_embed, GTK_TYPE_ALIGNMENT);
89
90 static void
91 gtk_champlain_embed_get_property (GObject *object,
92     guint prop_id,
93     GValue *value,
94     GParamSpec *pspec)
95 {
96   GtkChamplainEmbed *embed = GTK_CHAMPLAIN_EMBED (object);
97   GtkChamplainEmbedPrivate *priv = embed->priv;
98
99   switch(prop_id)
100     {
101       case PROP_VIEW:
102         g_value_set_object (value, priv->view);
103         break;
104       default:
105         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
106     }
107 }
108
109 static void
110 gtk_champlain_embed_set_property (GObject *object,
111     guint prop_id,
112     const GValue *value,
113     GParamSpec *pspec)
114 {
115   //GtkChamplainEmbed *embed = GTK_CHAMPLAIN_EMBED (object);
116   //GtkChamplainEmbedPrivate *priv = embed->priv;
117
118   switch(prop_id)
119   {
120     default:
121       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122   }
123 }
124
125 static void
126 gtk_champlain_embed_dispose (GObject *object)
127 {
128   GtkChamplainEmbed *embed = GTK_CHAMPLAIN_EMBED (object);
129   GtkChamplainEmbedPrivate *priv = embed->priv;
130
131   if (priv->view != NULL)
132     {
133       g_object_unref (priv->view);
134       priv->view = NULL;
135     }
136
137   if (priv->cursor_hand_open != NULL)
138     {
139       gdk_cursor_unref (priv->cursor_hand_open);
140       priv->cursor_hand_open = NULL;
141     }
142
143   if (priv->cursor_hand_closed != NULL)
144     {
145       gdk_cursor_unref (priv->cursor_hand_closed);
146       priv->cursor_hand_closed = NULL;
147     }
148
149   G_OBJECT_CLASS (gtk_champlain_embed_parent_class)->dispose (object);
150 }
151
152 static void
153 gtk_champlain_embed_finalize (GObject *object)
154 {
155   GtkChamplainEmbed *embed = GTK_CHAMPLAIN_EMBED (object);
156   GtkChamplainEmbedPrivate *priv = embed->priv;
157
158   G_OBJECT_CLASS (gtk_champlain_embed_parent_class)->finalize (object);
159 }
160
161 static void
162 gtk_champlain_embed_class_init (GtkChamplainEmbedClass *klass)
163 {
164   g_type_class_add_private (klass, sizeof (GtkChamplainEmbedPrivate));
165
166   GObjectClass *object_class = G_OBJECT_CLASS (klass);
167   object_class->finalize = gtk_champlain_embed_finalize;
168   object_class->dispose = gtk_champlain_embed_dispose;
169   object_class->get_property = gtk_champlain_embed_get_property;
170   object_class->set_property = gtk_champlain_embed_set_property;
171
172   /**
173   * GtkChamplainEmbed:champlain-view:
174   *
175   * The #ChamplainView to embed in the Gtk+ widget.
176   *
177   * Since: 0.4
178   */
179   g_object_class_install_property (object_class,
180       PROP_VIEW,
181       g_param_spec_object ("champlain-view",
182          "Champlain view",
183          "The ChamplainView to embed into the Gtk+ widget",
184          CHAMPLAIN_TYPE_VIEW,
185          G_PARAM_READABLE));
186 }
187
188 static void
189 set_view (GtkChamplainEmbed* embed,
190     ChamplainView *view)
191 {
192   GtkChamplainEmbedPrivate *priv = embed->priv;
193   ClutterActor *stage;
194
195   stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->clutter_embed));
196
197   if (priv->view != NULL)
198     {
199       g_object_unref (priv->view);
200       clutter_container_remove_actor (CLUTTER_CONTAINER (stage), CLUTTER_ACTOR (priv->view));
201     }
202
203   priv->view = g_object_ref (view);
204   clutter_actor_set_size (CLUTTER_ACTOR (priv->view), priv->width, priv->height);
205
206   clutter_container_add_actor (CLUTTER_CONTAINER (stage), CLUTTER_ACTOR (priv->view));
207 }
208
209 static void
210 gtk_champlain_embed_init (GtkChamplainEmbed *embed)
211 {
212   ClutterColor stage_color = { 0x34, 0x39, 0x39, 0xff };
213   ClutterActor *stage;
214
215   GtkChamplainEmbedPrivate *priv = GET_PRIVATE (embed);
216   embed->priv = priv;
217
218   priv->clutter_embed = gtk_clutter_embed_new ();
219
220   g_signal_connect (priv->clutter_embed,
221                     "size-allocate",
222                     G_CALLBACK (view_size_allocated_cb),
223                     embed);
224   g_signal_connect (priv->clutter_embed,
225                     "realize",
226                     G_CALLBACK (view_realize_cb),
227                     embed);
228   g_signal_connect (priv->clutter_embed,
229                     "button-press-event",
230                     G_CALLBACK (mouse_button_cb),
231                     embed);
232   g_signal_connect (priv->clutter_embed,
233                     "button-release-event",
234                     G_CALLBACK (mouse_button_cb),
235                     embed);
236   // Setup cursors
237   priv->cursor_hand_open = gdk_cursor_new (GDK_HAND1);
238   priv->cursor_hand_closed = gdk_cursor_new (GDK_FLEUR);
239
240   priv->view = NULL;
241   set_view (embed, CHAMPLAIN_VIEW (champlain_view_new ()));
242
243   // Setup stage
244   stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->clutter_embed));
245   clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
246
247   gtk_container_add (GTK_CONTAINER (embed), priv->clutter_embed);
248
249 }
250
251 static void
252 view_realize_cb (GtkWidget *widget,
253     GtkChamplainEmbed *view)
254 {
255   ClutterColor color = {0, 0, 0, };
256   GtkChamplainEmbedPrivate *priv = view->priv;
257
258   /* Setup mouse cursor to a hand */
259   gdk_window_set_cursor (gtk_widget_get_window (priv->clutter_embed), priv->cursor_hand_open);
260
261   /* Set selection color */
262   gtk_clutter_get_bg_color (GTK_WIDGET (widget), GTK_STATE_SELECTED, &color);
263   champlain_marker_set_highlight_color (&color);
264
265   gtk_clutter_get_text_color (GTK_WIDGET (widget), GTK_STATE_SELECTED, &color);
266   champlain_marker_set_highlight_text_color (&color);
267
268   /* To be added later: bg[active] (for selected markers, but focus is on another widget) */
269 }
270
271 static void
272 view_size_allocated_cb (GtkWidget *widget,
273     GtkAllocation *allocation,
274     GtkChamplainEmbed *view)
275 {
276   GtkChamplainEmbedPrivate *priv = view->priv;
277
278   if (priv->view != NULL)
279     clutter_actor_set_size (CLUTTER_ACTOR (priv->view), allocation->width, allocation->height);
280
281   priv->width = allocation->width;
282   priv->height = allocation->height;
283 }
284
285 static gboolean
286 mouse_button_cb (GtkWidget *widget,
287     GdkEventButton *event,
288     GtkChamplainEmbed *view)
289 {
290   GtkChamplainEmbedPrivate *priv = view->priv;
291
292   if (event->type == GDK_BUTTON_PRESS)
293     gdk_window_set_cursor (gtk_widget_get_window (priv->clutter_embed),
294                            priv->cursor_hand_closed);
295   else
296     gdk_window_set_cursor (gtk_widget_get_window (priv->clutter_embed),
297                            priv->cursor_hand_open);
298
299   return FALSE;
300 }
301
302 /**
303  * gtk_champlain_embed_new:
304  *
305  * Return value: a new #GtkChamplainEmbed ready to be used as a #GtkWidget.
306  *
307  * Since: 0.4
308  */
309 GtkWidget *
310 gtk_champlain_embed_new ()
311 {
312   return g_object_new (GTK_TYPE_CHAMPLAIN_EMBED, NULL);
313 }
314
315 /**
316  * gtk_champlain_embed_get_view:
317  * @embed: a #ChamplainView, the map view to embed
318  *
319  * Return value: a #ChamplainView ready to be used
320  *
321  * Since: 0.4
322  */
323 ChamplainView *
324 gtk_champlain_embed_get_view (GtkChamplainEmbed* embed)
325 {
326   g_return_val_if_fail (GTK_CHAMPLAIN_IS_EMBED(embed), NULL);
327
328   GtkChamplainEmbedPrivate *priv = embed->priv;
329   return priv->view;
330 }