Updated Spanish translation
[empathy-mirror.git] / src / empathy-mic-menu.c
blobf3d46b17e69c28eff8d379466adcbc1b458bebf4
1 /*
2 * Copyright (C) 2011 Collabora Ltd.
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.
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.
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
18 * GtkAction code based on gnome-terminal's TerminalTabsMenu object.
19 * Thanks guys!
22 #include <config.h>
24 #include <gtk/gtk.h>
26 #include "empathy-mic-menu.h"
27 #include "empathy-mic-monitor.h"
29 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
30 #include <libempathy/empathy-debug.h>
32 struct _EmpathyMicMenuPrivate
34 /* Borrowed ref; the call window actually owns us. */
35 EmpathyCallWindow *window;
37 /* Given away ref; the call window's UI manager now owns this. */
38 GtkActionGroup *action_group;
40 /* An invisible radio action so new microphones are always in the
41 * same radio group. */
42 GtkAction *anchor_action;
44 /* The merge ID used with the UI manager. We need to keep this
45 * around so in _clean we can remove all the items we've added
46 * before and start again. */
47 guint ui_id;
49 /* TRUE if we're in _update and so calling _set_active. */
50 gboolean in_update;
52 /* Queue of GtkRadioActions. */
53 GQueue *microphones;
55 EmpathyMicMonitor *mic_monitor;
58 G_DEFINE_TYPE (EmpathyMicMenu, empathy_mic_menu, G_TYPE_OBJECT);
60 #define MONITOR_KEY "empathy-mic-menu-is-monitor"
62 enum
64 PROP_WINDOW = 1,
67 static void empathy_mic_menu_update (EmpathyMicMenu *self);
69 static void
70 empathy_mic_menu_init (EmpathyMicMenu *self)
72 EmpathyMicMenuPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
73 EMPATHY_TYPE_MIC_MENU, EmpathyMicMenuPrivate);
75 self->priv = priv;
78 static void
79 empathy_mic_menu_set_property (GObject *object,
80 guint property_id,
81 const GValue *value,
82 GParamSpec *pspec)
84 EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
85 EmpathyMicMenuPrivate *priv = self->priv;
87 switch (property_id)
89 case PROP_WINDOW:
90 priv->window = g_value_get_object (value);
91 break;
92 default:
93 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
97 static void
98 empathy_mic_menu_get_property (GObject *object,
99 guint property_id,
100 GValue *value,
101 GParamSpec *pspec)
103 EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
104 EmpathyMicMenuPrivate *priv = self->priv;
106 switch (property_id)
108 case PROP_WINDOW:
109 g_value_set_object (value, priv->window);
110 break;
111 default:
112 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
116 static void
117 empathy_mic_menu_clean (EmpathyMicMenu *self)
119 EmpathyMicMenuPrivate *priv = self->priv;
120 GtkUIManager *ui_manager;
122 if (priv->ui_id == 0)
123 return;
125 ui_manager = empathy_call_window_get_ui_manager (priv->window);
127 gtk_ui_manager_remove_ui (ui_manager, priv->ui_id);
128 gtk_ui_manager_ensure_update (ui_manager);
129 priv->ui_id = 0;
132 static void
133 empathy_mic_menu_change_mic_cb (GObject *source_object,
134 GAsyncResult *result,
135 gpointer user_data)
137 EmpathyGstAudioSrc *audio = EMPATHY_GST_AUDIO_SRC (source_object);
138 EmpathyMicMenu *self = user_data;
139 GError *error = NULL;
141 if (!empathy_audio_src_change_microphone_finish (audio, result, &error))
143 DEBUG ("Failed to change microphone: %s", error->message);
144 g_clear_error (&error);
146 /* We call update here because if this change operation failed
147 * and we don't update the menu items, it'll point to the wrong
148 * device. We don't want to call it if the change was successful
149 * because we'll get the notify::microphone signal fired in a
150 * bit and the current value hasn't changed so it'd keep jumping
151 * between these values like there's no tomorrow, etc. */
152 empathy_mic_menu_update (self);
156 static void
157 empathy_mic_menu_activate_cb (GtkToggleAction *action,
158 EmpathyMicMenu *self)
160 EmpathyMicMenuPrivate *priv = self->priv;
161 EmpathyGstAudioSrc *audio;
162 gint value;
164 if (priv->in_update)
165 return;
167 audio = empathy_call_window_get_audio_src (priv->window);
169 g_object_get (action, "value", &value, NULL);
171 empathy_audio_src_change_microphone_async (audio, value,
172 empathy_mic_menu_change_mic_cb, self);
175 static void
176 empathy_mic_menu_update (EmpathyMicMenu *self)
178 EmpathyMicMenuPrivate *priv = self->priv;
179 GList *l;
180 GtkUIManager *ui_manager;
181 EmpathyGstAudioSrc *audio;
182 guint current_mic;
184 ui_manager = empathy_call_window_get_ui_manager (priv->window);
186 audio = empathy_call_window_get_audio_src (priv->window);
187 current_mic = empathy_audio_src_get_microphone (audio);
189 empathy_mic_menu_clean (self);
190 priv->ui_id = gtk_ui_manager_new_merge_id (ui_manager);
192 for (l = priv->microphones->head; l != NULL; l = l->next)
194 GtkRadioAction *action = l->data;
195 const gchar *name = gtk_action_get_name (GTK_ACTION (action));
196 gint value;
197 gboolean active;
199 g_object_get (action, "value", &value, NULL);
201 active = (value == (gint) current_mic);
203 if (active)
205 priv->in_update = TRUE;
206 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
207 priv->in_update = FALSE;
210 /* If action is a monitor then don't show it in the UI, BUT do
211 * display it regardless if it is the current device. This is so
212 * we don't have a rubbish UI by showing monitor devices in
213 * Empathy, but still show the correct device when someone plays
214 * with pavucontrol. */
215 if (g_object_get_data (G_OBJECT (action), MONITOR_KEY) != NULL
216 && !active)
217 continue;
219 gtk_ui_manager_add_ui (ui_manager, priv->ui_id,
220 /* TODO: this should probably be passed from the call
221 * window, seeing that it's a reference to
222 * empathy-call-window.ui. */
223 "/menubar1/edit/menumicrophone",
224 name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
228 static void
229 empathy_mic_menu_add_microphone (EmpathyMicMenu *self,
230 const gchar *name,
231 const gchar *description,
232 guint source_idx,
233 gboolean is_monitor)
235 EmpathyMicMenuPrivate *priv = self->priv;
236 GtkRadioAction *action;
237 GSList *group;
239 action = gtk_radio_action_new (name, description, NULL, NULL, source_idx);
240 gtk_action_group_add_action_with_accel (priv->action_group,
241 GTK_ACTION (action), NULL);
243 /* Set MONITOR_KEY on the action to non-NULL if it's a monitor
244 * because we don't want to show monitors if we can help it. */
245 if (is_monitor)
247 g_object_set_data (G_OBJECT (action), MONITOR_KEY,
248 GUINT_TO_POINTER (TRUE));
251 group = gtk_radio_action_get_group (GTK_RADIO_ACTION (priv->anchor_action));
252 gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
254 g_queue_push_tail (priv->microphones, action);
256 g_signal_connect (action, "activate",
257 G_CALLBACK (empathy_mic_menu_activate_cb), self);
260 static void
261 empathy_mic_menu_notify_microphone_cb (EmpathyGstAudioSrc *audio,
262 GParamSpec *pspec,
263 EmpathyMicMenu *self)
265 empathy_mic_menu_update (self);
268 static void
269 empathy_mic_menu_microphone_added_cb (EmpathyMicMonitor *monitor,
270 guint source_idx,
271 const gchar *name,
272 const gchar *description,
273 gboolean is_monitor,
274 EmpathyMicMenu *self)
276 empathy_mic_menu_add_microphone (self, name, description,
277 source_idx, is_monitor);
279 empathy_mic_menu_update (self);
282 static void
283 empathy_mic_menu_microphone_removed_cb (EmpathyMicMonitor *monitor,
284 guint source_idx,
285 EmpathyMicMenu *self)
287 EmpathyMicMenuPrivate *priv = self->priv;
288 GList *l;
290 for (l = priv->microphones->head; l != NULL; l = l->next)
292 GtkRadioAction *action = l->data;
293 gint value;
295 g_object_get (action, "value", &value, NULL);
297 if (value != (gint) source_idx)
299 action = NULL;
300 continue;
303 g_signal_handlers_disconnect_by_func (action,
304 G_CALLBACK (empathy_mic_menu_activate_cb), self);
306 gtk_action_group_remove_action (priv->action_group, GTK_ACTION (action));
307 g_queue_remove (priv->microphones, action);
308 break;
311 empathy_mic_menu_update (self);
314 static void
315 empathy_mic_menu_list_microphones_cb (GObject *source_object,
316 GAsyncResult *result,
317 gpointer user_data)
319 EmpathyMicMonitor *monitor = EMPATHY_MIC_MONITOR (source_object);
320 EmpathyMicMenu *self = user_data;
321 GError *error = NULL;
322 const GList *mics = NULL;
324 mics = empathy_mic_monitor_list_microphones_finish (monitor, result, &error);
326 if (error != NULL)
328 DEBUG ("Failed to get microphone list: %s", error->message);
329 g_clear_error (&error);
330 return;
333 for (; mics != NULL; mics = mics->next)
335 EmpathyMicrophone *mic = mics->data;
337 empathy_mic_menu_add_microphone (self, mic->name,
338 mic->description, mic->index, mic->is_monitor);
341 empathy_mic_menu_update (self);
344 static void
345 empathy_mic_menu_constructed (GObject *obj)
347 EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
348 EmpathyMicMenuPrivate *priv = self->priv;
349 GtkUIManager *ui_manager;
350 EmpathyGstAudioSrc *audio;
352 g_assert (EMPATHY_IS_CALL_WINDOW (priv->window));
354 ui_manager = empathy_call_window_get_ui_manager (priv->window);
355 audio = empathy_call_window_get_audio_src (priv->window);
357 g_assert (GTK_IS_UI_MANAGER (ui_manager));
358 g_assert (EMPATHY_IS_GST_AUDIO_SRC (audio));
360 /* Okay let's go go go. */
362 priv->mic_monitor = empathy_mic_monitor_new ();
364 priv->action_group = gtk_action_group_new ("EmpathyMicMenu");
365 gtk_ui_manager_insert_action_group (ui_manager, priv->action_group, -1);
366 /* the UI manager now owns this */
367 g_object_unref (priv->action_group);
369 priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
370 "name", "EmpathyMicMenuAnchorAction",
371 NULL);
372 gtk_action_group_add_action (priv->action_group, priv->anchor_action);
373 g_object_unref (priv->anchor_action);
375 priv->microphones = g_queue_new ();
377 /* Don't bother with any of this if we don't support changing
378 * microphone, so don't listen for microphone changes or enumerate
379 * the available microphones. */
380 if (!empathy_audio_src_supports_changing_mic (audio))
381 return;
383 tp_g_signal_connect_object (audio, "notify::microphone",
384 G_CALLBACK (empathy_mic_menu_notify_microphone_cb), self, 0);
385 tp_g_signal_connect_object (priv->mic_monitor, "microphone-added",
386 G_CALLBACK (empathy_mic_menu_microphone_added_cb), self, 0);
387 tp_g_signal_connect_object (priv->mic_monitor, "microphone-removed",
388 G_CALLBACK (empathy_mic_menu_microphone_removed_cb), self, 0);
390 empathy_mic_monitor_list_microphones_async (priv->mic_monitor,
391 empathy_mic_menu_list_microphones_cb, self);
394 static void
395 empathy_mic_menu_dispose (GObject *obj)
397 EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
398 EmpathyMicMenuPrivate *priv = self->priv;
400 if (priv->microphones != NULL)
401 g_queue_free (priv->microphones);
402 priv->microphones = NULL;
404 tp_clear_object (&priv->mic_monitor);
406 G_OBJECT_CLASS (empathy_mic_menu_parent_class)->dispose (obj);
409 static void
410 empathy_mic_menu_class_init (EmpathyMicMenuClass *klass)
412 GObjectClass *object_class = G_OBJECT_CLASS (klass);
414 object_class->set_property = empathy_mic_menu_set_property;
415 object_class->get_property = empathy_mic_menu_get_property;
416 object_class->constructed = empathy_mic_menu_constructed;
417 object_class->dispose = empathy_mic_menu_dispose;
419 g_object_class_install_property (object_class, PROP_WINDOW,
420 g_param_spec_object ("window", "window", "window",
421 EMPATHY_TYPE_CALL_WINDOW,
422 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
424 g_type_class_add_private (object_class, sizeof (EmpathyMicMenuPrivate));
427 EmpathyMicMenu *
428 empathy_mic_menu_new (EmpathyCallWindow *window)
430 return g_object_new (EMPATHY_TYPE_MIC_MENU,
431 "window", window,
432 NULL);