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.
26 #include "empathy-mic-menu.h"
28 struct _EmpathyMicMenuPrivate
30 /* Borrowed ref; the call window actually owns us. */
31 EmpathyCallWindow *window;
33 /* Given away ref; the call window's UI manager now owns this. */
34 GtkActionGroup *action_group;
36 /* An invisible radio action so new microphones are always in the
37 * same radio group. */
38 GtkAction *anchor_action;
40 /* The merge ID used with the UI manager. We need to keep this
41 * around so in _clean we can remove all the items we've added
42 * before and start again. */
45 /* TRUE if we're in _update and so calling _set_active. */
48 /* Queue of GtkRadioActions. */
52 G_DEFINE_TYPE (EmpathyMicMenu, empathy_mic_menu, G_TYPE_OBJECT);
54 #define MONITOR_KEY "empathy-mic-menu-is-monitor"
61 static void empathy_mic_menu_update (EmpathyMicMenu *self);
64 empathy_mic_menu_init (EmpathyMicMenu *self)
66 EmpathyMicMenuPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
67 EMPATHY_TYPE_MIC_MENU, EmpathyMicMenuPrivate);
73 empathy_mic_menu_set_property (GObject *object,
78 EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
79 EmpathyMicMenuPrivate *priv = self->priv;
84 priv->window = g_value_get_object (value);
87 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
92 empathy_mic_menu_get_property (GObject *object,
97 EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
98 EmpathyMicMenuPrivate *priv = self->priv;
103 g_value_set_object (value, priv->window);
106 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
111 empathy_mic_menu_clean (EmpathyMicMenu *self)
113 EmpathyMicMenuPrivate *priv = self->priv;
114 GtkUIManager *ui_manager;
116 if (priv->ui_id == 0)
119 ui_manager = empathy_call_window_get_ui_manager (priv->window);
121 gtk_ui_manager_remove_ui (ui_manager, priv->ui_id);
122 gtk_ui_manager_ensure_update (ui_manager);
127 empathy_mic_menu_change_mic_cb (GObject *source_object,
128 GAsyncResult *result,
131 EmpathyGstAudioSrc *audio = EMPATHY_GST_AUDIO_SRC (source_object);
132 EmpathyMicMenu *self = user_data;
133 GError *error = NULL;
135 if (!empathy_audio_src_change_microphone_finish (audio, result, &error))
137 g_debug ("Failed to change microphone: %s", error->message);
138 g_clear_error (&error);
140 /* We call update here because if this change operation failed
141 * and we don't update the menu items, it'll point to the wrong
142 * device. We don't want to call it if the change was successful
143 * because we'll get the notify::microphone signal fired in a
144 * bit and the current value hasn't changed so it'd keep jumping
145 * between these values like there's no tomorrow, etc. */
146 empathy_mic_menu_update (self);
151 empathy_mic_menu_activate_cb (GtkToggleAction *action,
152 EmpathyMicMenu *self)
154 EmpathyMicMenuPrivate *priv = self->priv;
155 EmpathyGstAudioSrc *audio;
161 audio = empathy_call_window_get_audio_src (priv->window);
163 g_object_get (action, "value", &value, NULL);
165 empathy_audio_src_change_microphone_async (audio, value,
166 empathy_mic_menu_change_mic_cb, self);
170 empathy_mic_menu_update (EmpathyMicMenu *self)
172 EmpathyMicMenuPrivate *priv = self->priv;
174 GtkUIManager *ui_manager;
175 EmpathyGstAudioSrc *audio;
178 ui_manager = empathy_call_window_get_ui_manager (priv->window);
180 audio = empathy_call_window_get_audio_src (priv->window);
181 current_mic = empathy_audio_src_get_microphone (audio);
183 empathy_mic_menu_clean (self);
184 priv->ui_id = gtk_ui_manager_new_merge_id (ui_manager);
186 for (l = priv->microphones->head; l != NULL; l = l->next)
188 GtkRadioAction *action = l->data;
189 const gchar *name = gtk_action_get_name (GTK_ACTION (action));
193 g_object_get (action, "value", &value, NULL);
195 active = (value == (gint) current_mic);
199 priv->in_update = TRUE;
200 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
201 priv->in_update = FALSE;
204 /* If action is a monitor then don't show it in the UI, BUT do
205 * display it regardless if it is the current device. This is so
206 * we don't have a rubbish UI by showing monitor devices in
207 * Empathy, but still show the correct device when someone plays
208 * with pavucontrol. */
209 if (g_object_get_data (G_OBJECT (action), MONITOR_KEY) != NULL
213 gtk_ui_manager_add_ui (ui_manager, priv->ui_id,
214 /* TODO: this should probably be passed from the call
215 * window, seeing that it's a reference to
216 * empathy-call-window.ui. */
217 "/menubar1/edit/menumicrophone",
218 name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
223 empathy_mic_menu_add_microphone (EmpathyMicMenu *self,
225 const gchar *description,
229 EmpathyMicMenuPrivate *priv = self->priv;
230 GtkRadioAction *action;
233 action = gtk_radio_action_new (name, description, NULL, NULL, source_idx);
234 gtk_action_group_add_action_with_accel (priv->action_group,
235 GTK_ACTION (action), NULL);
237 /* Set MONITOR_KEY on the action to non-NULL if it's a monitor
238 * because we don't want to show monitors if we can help it. */
241 g_object_set_data (G_OBJECT (action), MONITOR_KEY,
242 GUINT_TO_POINTER (TRUE));
245 group = gtk_radio_action_get_group (GTK_RADIO_ACTION (priv->anchor_action));
246 gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
248 g_queue_push_tail (priv->microphones, action);
250 g_signal_connect (action, "activate",
251 G_CALLBACK (empathy_mic_menu_activate_cb), self);
255 empathy_mic_menu_notify_microphone_cb (EmpathyGstAudioSrc *audio,
257 EmpathyMicMenu *self)
259 empathy_mic_menu_update (self);
263 empathy_mic_menu_microphone_added_cb (EmpathyGstAudioSrc *audio,
266 const gchar *description,
268 EmpathyMicMenu *self)
270 empathy_mic_menu_add_microphone (self, name, description,
271 source_idx, is_monitor);
273 empathy_mic_menu_update (self);
277 empathy_mic_menu_microphone_removed_cb (EmpathyGstAudioSrc *audio,
279 EmpathyMicMenu *self)
281 EmpathyMicMenuPrivate *priv = self->priv;
284 for (l = priv->microphones->head; l != NULL; l = l->next)
286 GtkRadioAction *action = l->data;
289 g_object_get (action, "value", &value, NULL);
291 if (value != (gint) source_idx)
297 g_signal_handlers_disconnect_by_func (action,
298 G_CALLBACK (empathy_mic_menu_activate_cb), self);
300 gtk_action_group_remove_action (priv->action_group, GTK_ACTION (action));
301 g_queue_remove (priv->microphones, action);
305 empathy_mic_menu_update (self);
309 empathy_mic_menu_get_microphones_cb (GObject *source_object,
310 GAsyncResult *result,
313 EmpathyGstAudioSrc *audio = EMPATHY_GST_AUDIO_SRC (source_object);
314 EmpathyMicMenu *self = user_data;
315 GError *error = NULL;
316 const GList *mics = NULL;
318 mics = empathy_audio_src_get_microphones_finish (audio, result, &error);
322 g_debug ("Failed to get microphone list: %s", error->message);
323 g_clear_error (&error);
327 for (; mics != NULL; mics = mics->next)
329 EmpathyAudioSrcMicrophone *mic = mics->data;
331 empathy_mic_menu_add_microphone (self, mic->name,
332 mic->description, mic->index, mic->is_monitor);
335 empathy_mic_menu_update (self);
339 empathy_mic_menu_constructed (GObject *obj)
341 EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
342 EmpathyMicMenuPrivate *priv = self->priv;
343 GtkUIManager *ui_manager;
344 EmpathyGstAudioSrc *audio;
346 g_assert (EMPATHY_IS_CALL_WINDOW (priv->window));
348 ui_manager = empathy_call_window_get_ui_manager (priv->window);
349 audio = empathy_call_window_get_audio_src (priv->window);
351 g_assert (GTK_IS_UI_MANAGER (ui_manager));
352 g_assert (EMPATHY_IS_GST_AUDIO_SRC (audio));
354 /* Okay let's go go go. */
356 priv->action_group = gtk_action_group_new ("EmpathyMicMenu");
357 gtk_ui_manager_insert_action_group (ui_manager, priv->action_group, -1);
358 /* the UI manager now owns this */
359 g_object_unref (priv->action_group);
361 priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
362 "name", "EmpathyMicMenuAnchorAction",
364 gtk_action_group_add_action (priv->action_group, priv->anchor_action);
365 g_object_unref (priv->anchor_action);
367 tp_g_signal_connect_object (audio, "notify::microphone",
368 G_CALLBACK (empathy_mic_menu_notify_microphone_cb), self, 0);
369 tp_g_signal_connect_object (audio, "microphone-added",
370 G_CALLBACK (empathy_mic_menu_microphone_added_cb), self, 0);
371 tp_g_signal_connect_object (audio, "microphone-removed",
372 G_CALLBACK (empathy_mic_menu_microphone_removed_cb), self, 0);
374 priv->microphones = g_queue_new ();
376 empathy_audio_src_get_microphones_async (audio,
377 empathy_mic_menu_get_microphones_cb, self);
381 empathy_mic_menu_dispose (GObject *obj)
383 EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
384 EmpathyMicMenuPrivate *priv = self->priv;
386 if (priv->microphones != NULL)
387 g_queue_free (priv->microphones);
388 priv->microphones = NULL;
390 G_OBJECT_CLASS (empathy_mic_menu_parent_class)->dispose (obj);
394 empathy_mic_menu_class_init (EmpathyMicMenuClass *klass)
396 GObjectClass *object_class = G_OBJECT_CLASS (klass);
398 object_class->set_property = empathy_mic_menu_set_property;
399 object_class->get_property = empathy_mic_menu_get_property;
400 object_class->constructed = empathy_mic_menu_constructed;
401 object_class->dispose = empathy_mic_menu_dispose;
403 g_object_class_install_property (object_class, PROP_WINDOW,
404 g_param_spec_object ("window", "window", "window",
405 EMPATHY_TYPE_CALL_WINDOW,
406 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
408 g_type_class_add_private (object_class, sizeof (EmpathyMicMenuPrivate));
412 empathy_mic_menu_new (EmpathyCallWindow *window)
414 return g_object_new (EMPATHY_TYPE_MIC_MENU,