]> git.0d.be Git - empathy.git/blob - src/empathy-camera-menu.c
account-chooser: use tp_g_signal_connect_object()
[empathy.git] / src / empathy-camera-menu.c
1 /*
2  * Copyright (C) 2011 Collabora Ltd.
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  * GtkAction code based on gnome-terminal's TerminalTabsMenu object.
19  * Thanks guys!
20  */
21
22 #include <config.h>
23
24 #include <gtk/gtk.h>
25
26 #include <libempathy/empathy-camera-monitor.h>
27 #include <libempathy/empathy-gsettings.h>
28
29 #include "empathy-camera-menu.h"
30
31 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
32 #include <libempathy/empathy-debug.h>
33
34 struct _EmpathyCameraMenuPrivate
35 {
36   /* Borrowed ref; the call window actually owns us. */
37   EmpathyCallWindow *window;
38
39   /* Given away ref; the call window's UI manager now owns this. */
40   GtkActionGroup *action_group;
41
42   /* An invisible radio action so new cameras are always in the
43    * same radio group. */
44   GtkAction *anchor_action;
45
46   /* The merge ID used with the UI manager. We need to keep this
47    * around so in _clean we can remove all the items we've added
48    * before and start again. */
49   guint ui_id;
50
51   /* TRUE if we're in _update and so calling _set_active. */
52   gboolean in_update;
53
54   /* Queue of GtkRadioActions. */
55   GQueue *cameras;
56
57   EmpathyCameraMonitor *camera_monitor;
58
59   GSettings *settings;
60 };
61
62 G_DEFINE_TYPE (EmpathyCameraMenu, empathy_camera_menu, G_TYPE_OBJECT);
63
64 enum
65 {
66   PROP_WINDOW = 1,
67 };
68
69 static void empathy_camera_menu_update (EmpathyCameraMenu *self);
70
71 static void
72 empathy_camera_menu_init (EmpathyCameraMenu *self)
73 {
74   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
75     EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenuPrivate);
76 }
77
78 static void
79 empathy_camera_menu_set_property (GObject *object,
80     guint property_id,
81     const GValue *value,
82     GParamSpec *pspec)
83 {
84   EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (object);
85
86   switch (property_id)
87     {
88       case PROP_WINDOW:
89         self->priv->window = g_value_get_object (value);
90         break;
91       default:
92         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
93     }
94 }
95
96 static void
97 empathy_camera_menu_get_property (GObject *object,
98     guint property_id,
99     GValue *value,
100     GParamSpec *pspec)
101 {
102   EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (object);
103
104   switch (property_id)
105     {
106       case PROP_WINDOW:
107         g_value_set_object (value, self->priv->window);
108         break;
109       default:
110         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
111     }
112 }
113
114 static void
115 empathy_camera_menu_clean (EmpathyCameraMenu *self)
116 {
117   GtkUIManager *ui_manager;
118
119   if (self->priv->ui_id == 0)
120     return;
121
122   ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
123
124   gtk_ui_manager_remove_ui (ui_manager, self->priv->ui_id);
125   gtk_ui_manager_ensure_update (ui_manager);
126   self->priv->ui_id = 0;
127 }
128
129 static void
130 empathy_camera_menu_activate_cb (GtkAction *action,
131     EmpathyCameraMenu *self)
132 {
133   EmpathyGstVideoSrc *video;
134   const gchar *device;
135   gchar *current_device;
136
137   if (self->priv->in_update)
138     return;
139
140   video = empathy_call_window_get_video_src (self->priv->window);
141
142   device = gtk_action_get_name (action);
143   current_device = empathy_video_src_dup_device (video);
144
145   /* Don't change the device if it's the currently used one */
146   if (!tp_strdiff (device, current_device))
147     goto out;
148
149   empathy_call_window_play_camera (self->priv->window, FALSE);
150   empathy_video_src_change_device (video, device);
151   empathy_call_window_play_camera (self->priv->window, TRUE);
152
153  out:
154   g_free (current_device);
155 }
156
157 static void
158 empathy_camera_menu_update (EmpathyCameraMenu *self)
159 {
160   GList *l;
161   GtkUIManager *ui_manager;
162   EmpathyGstVideoSrc *video;
163   gchar *current_camera;
164
165   ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
166
167   video = empathy_call_window_get_video_src (self->priv->window);
168   current_camera = empathy_video_src_dup_device (video);
169
170   empathy_camera_menu_clean (self);
171   self->priv->ui_id = gtk_ui_manager_new_merge_id (ui_manager);
172
173   for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
174     {
175       GtkRadioAction *action = l->data;
176       const gchar *name = gtk_action_get_name (GTK_ACTION (action));
177
178       if (!tp_strdiff (current_camera, name))
179         {
180           self->priv->in_update = TRUE;
181           gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
182           self->priv->in_update = FALSE;
183         }
184
185       gtk_ui_manager_add_ui (ui_manager, self->priv->ui_id,
186           /* TODO: this should probably be passed from the call
187            * window, seeing that it's a reference to
188            * empathy-call-window.ui. */
189           "/menubar1/edit/menucamera",
190           name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
191     }
192
193   g_free (current_camera);
194 }
195
196 static void
197 empathy_camera_menu_add_camera (EmpathyCameraMenu *self,
198     EmpathyCamera *camera)
199 {
200   GtkRadioAction *action;
201   GSList *group;
202
203   action = gtk_radio_action_new (camera->device, camera->name, NULL, NULL, 0);
204   gtk_action_group_add_action (self->priv->action_group, GTK_ACTION (action));
205
206   group = gtk_radio_action_get_group (
207       GTK_RADIO_ACTION (self->priv->anchor_action));
208   gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
209
210   g_queue_push_tail (self->priv->cameras, action);
211
212   g_signal_connect (action, "activate",
213       G_CALLBACK (empathy_camera_menu_activate_cb), self);
214 }
215
216 static void
217 empathy_camera_menu_camera_added_cb (EmpathyCameraMonitor *monitor,
218     EmpathyCamera *camera,
219     EmpathyCameraMenu *self)
220 {
221   empathy_camera_menu_add_camera (self, camera);
222   empathy_camera_menu_update (self);
223 }
224
225 static void
226 empathy_camera_menu_camera_removed_cb (EmpathyCameraMonitor *monitor,
227     EmpathyCamera *camera,
228     EmpathyCameraMenu *self)
229 {
230   GList *l;
231
232   for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
233     {
234       GtkAction *action = l->data;
235       const gchar *device;
236
237       device = gtk_action_get_name (action);
238
239       if (tp_strdiff (device, camera->device))
240         continue;
241
242       g_signal_handlers_disconnect_by_func (action,
243           G_CALLBACK (empathy_camera_menu_activate_cb), self);
244
245       gtk_action_group_remove_action (self->priv->action_group,
246           action);
247       g_queue_remove (self->priv->cameras, action);
248       break;
249     }
250
251   empathy_camera_menu_update (self);
252 }
253
254 static void
255 empathy_camera_menu_prefs_camera_changed_cb (GSettings *settings,
256     gchar *key,
257     EmpathyCameraMenu *self)
258 {
259   gchar *device = g_settings_get_string (settings, key);
260   GtkRadioAction *action = NULL;
261   gboolean found = FALSE;
262   GList *l;
263
264   for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
265     {
266       const gchar *name;
267
268       action = l->data;
269       name = gtk_action_get_name (GTK_ACTION (action));
270
271       if (!tp_strdiff (device, name))
272         {
273           found = TRUE;
274           break;
275         }
276     }
277
278   /* If the selected camera isn't found, we connect the first
279    * available one */
280   if (!found && self->priv->cameras->head != NULL)
281     action = self->priv->cameras->head->data;
282
283   if (action != NULL &&
284       !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
285     {
286       g_signal_handlers_block_by_func (settings,
287           empathy_camera_menu_prefs_camera_changed_cb, self);
288       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
289       g_signal_handlers_unblock_by_func (settings,
290           empathy_camera_menu_prefs_camera_changed_cb, self);
291     }
292
293   g_free (device);
294 }
295
296 static void
297 empathy_camera_menu_get_cameras (EmpathyCameraMenu *self)
298 {
299   const GList *cameras;
300
301   cameras = empathy_camera_monitor_get_cameras (self->priv->camera_monitor);
302
303   for (; cameras != NULL; cameras = g_list_next (cameras))
304     {
305       EmpathyCamera *camera = cameras->data;
306
307       empathy_camera_menu_add_camera (self, camera);
308     }
309
310   empathy_camera_menu_update (self);
311
312   /* Do as if the gsettings key had changed, so we select the key that
313    * was last set. */
314   empathy_camera_menu_prefs_camera_changed_cb (self->priv->settings,
315       EMPATHY_PREFS_CALL_CAMERA_DEVICE, self);
316 }
317
318 static void
319 empathy_camera_menu_constructed (GObject *obj)
320 {
321   EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (obj);
322   GtkUIManager *ui_manager;
323
324   g_assert (EMPATHY_IS_CALL_WINDOW (self->priv->window));
325
326   ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
327
328   g_assert (GTK_IS_UI_MANAGER (ui_manager));
329
330   /* Okay let's go go go. */
331
332   self->priv->action_group = gtk_action_group_new ("EmpathyCameraMenu");
333   gtk_ui_manager_insert_action_group (ui_manager, self->priv->action_group, -1);
334   /* the UI manager now owns this */
335   g_object_unref (self->priv->action_group);
336
337   self->priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
338       "name", "EmpathyCameraMenuAnchorAction",
339       NULL);
340   gtk_action_group_add_action (self->priv->action_group,
341       self->priv->anchor_action);
342   g_object_unref (self->priv->anchor_action);
343
344   self->priv->camera_monitor = empathy_camera_monitor_new ();
345
346   tp_g_signal_connect_object (self->priv->camera_monitor, "added",
347       G_CALLBACK (empathy_camera_menu_camera_added_cb), self, 0);
348   tp_g_signal_connect_object (self->priv->camera_monitor, "removed",
349       G_CALLBACK (empathy_camera_menu_camera_removed_cb), self, 0);
350
351   self->priv->settings = g_settings_new (EMPATHY_PREFS_CALL_SCHEMA);
352   g_signal_connect (self->priv->settings,
353       "changed::"EMPATHY_PREFS_CALL_CAMERA_DEVICE,
354       G_CALLBACK (empathy_camera_menu_prefs_camera_changed_cb), self);
355
356   self->priv->cameras = g_queue_new ();
357
358   empathy_camera_menu_get_cameras (self);
359 }
360
361 static void
362 empathy_camera_menu_dispose (GObject *obj)
363 {
364   EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (obj);
365
366   tp_clear_pointer (&self->priv->cameras, g_queue_free);
367
368   tp_clear_object (&self->priv->camera_monitor);
369   tp_clear_object (&self->priv->settings);
370
371   G_OBJECT_CLASS (empathy_camera_menu_parent_class)->dispose (obj);
372 }
373
374 static void
375 empathy_camera_menu_class_init (EmpathyCameraMenuClass *klass)
376 {
377   GObjectClass *object_class = G_OBJECT_CLASS (klass);
378
379   object_class->set_property = empathy_camera_menu_set_property;
380   object_class->get_property = empathy_camera_menu_get_property;
381   object_class->constructed = empathy_camera_menu_constructed;
382   object_class->dispose = empathy_camera_menu_dispose;
383
384   g_object_class_install_property (object_class, PROP_WINDOW,
385       g_param_spec_object ("window", "window", "window",
386           EMPATHY_TYPE_CALL_WINDOW,
387           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
388
389   g_type_class_add_private (object_class, sizeof (EmpathyCameraMenuPrivate));
390 }
391
392 EmpathyCameraMenu *
393 empathy_camera_menu_new (EmpathyCallWindow *window)
394 {
395   return g_object_new (EMPATHY_TYPE_CAMERA_MENU,
396       "window", window,
397       NULL);
398 }