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);
59 static void empathy_mic_menu_update (EmpathyMicMenu *self);
62 empathy_mic_menu_init (EmpathyMicMenu *self)
64 EmpathyMicMenuPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
65 EMPATHY_TYPE_MIC_MENU, EmpathyMicMenuPrivate);
71 empathy_mic_menu_set_property (GObject *object,
76 EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
77 EmpathyMicMenuPrivate *priv = self->priv;
82 priv->window = g_value_get_object (value);
85 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
90 empathy_mic_menu_get_property (GObject *object,
95 EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
96 EmpathyMicMenuPrivate *priv = self->priv;
101 g_value_set_object (value, priv->window);
104 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109 empathy_mic_menu_clean (EmpathyMicMenu *self)
111 EmpathyMicMenuPrivate *priv = self->priv;
112 GtkUIManager *ui_manager;
114 if (priv->ui_id == 0)
117 ui_manager = empathy_call_window_get_ui_manager (priv->window);
119 gtk_ui_manager_remove_ui (ui_manager, priv->ui_id);
120 gtk_ui_manager_ensure_update (ui_manager);
125 empathy_mic_menu_change_mic_cb (GObject *source_object,
126 GAsyncResult *result,
129 EmpathyGstAudioSrc *audio = EMPATHY_GST_AUDIO_SRC (source_object);
130 EmpathyMicMenu *self = user_data;
131 GError *error = NULL;
133 if (!empathy_audio_src_change_microphone_finish (audio, result, &error))
135 g_debug ("Failed to change microphone: %s", error->message);
136 g_clear_error (&error);
138 /* We call update here because if this change operation failed
139 * and we don't update the menu items, it'll point to the wrong
140 * device. We don't want to call it if the change was successful
141 * because we'll get the notify::microphone signal fired in a
142 * bit and the current value hasn't changed so it'd keep jumping
143 * between these values like there's no tomorrow, etc. */
144 empathy_mic_menu_update (self);
149 empathy_mic_menu_activate_cb (GtkToggleAction *action,
150 EmpathyMicMenu *self)
152 EmpathyMicMenuPrivate *priv = self->priv;
153 EmpathyGstAudioSrc *audio;
159 audio = empathy_call_window_get_audio_src (priv->window);
161 g_object_get (action, "value", &value, NULL);
163 empathy_audio_src_change_microphone_async (audio, value,
164 empathy_mic_menu_change_mic_cb, self);
168 empathy_mic_menu_update (EmpathyMicMenu *self)
170 EmpathyMicMenuPrivate *priv = self->priv;
172 GtkUIManager *ui_manager;
173 EmpathyGstAudioSrc *audio;
176 ui_manager = empathy_call_window_get_ui_manager (priv->window);
178 audio = empathy_call_window_get_audio_src (priv->window);
179 current_mic = empathy_audio_src_get_microphone (audio);
181 empathy_mic_menu_clean (self);
182 priv->ui_id = gtk_ui_manager_new_merge_id (ui_manager);
184 for (l = priv->microphones->head; l != NULL; l = l->next)
186 GtkRadioAction *action = l->data;
187 const gchar *name = gtk_action_get_name (GTK_ACTION (action));
190 g_object_get (action, "value", &value, NULL);
192 if (value == (gint) current_mic)
194 priv->in_update = TRUE;
195 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
196 priv->in_update = FALSE;
199 gtk_ui_manager_add_ui (ui_manager, priv->ui_id,
200 /* TODO: this should probably be passed from the call
201 * window, seeing that it's a reference to
202 * empathy-call-window.ui. */
203 "/menubar1/edit/menumicrophone",
204 name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
209 empathy_mic_menu_add_microphone (EmpathyMicMenu *self,
211 const gchar *description,
214 EmpathyMicMenuPrivate *priv = self->priv;
215 GtkRadioAction *action;
218 action = gtk_radio_action_new (name, description, NULL, NULL, source_idx);
219 gtk_action_group_add_action_with_accel (priv->action_group,
220 GTK_ACTION (action), NULL);
222 group = gtk_radio_action_get_group (GTK_RADIO_ACTION (priv->anchor_action));
223 gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
225 g_queue_push_tail (priv->microphones, action);
227 g_signal_connect (action, "activate",
228 G_CALLBACK (empathy_mic_menu_activate_cb), self);
232 empathy_mic_menu_notify_microphone_cb (EmpathyGstAudioSrc *audio,
234 EmpathyMicMenu *self)
236 empathy_mic_menu_update (self);
240 empathy_mic_menu_microphone_added_cb (EmpathyGstAudioSrc *audio,
243 const gchar *description,
244 EmpathyMicMenu *self)
246 empathy_mic_menu_add_microphone (self, name, description, source_idx);
248 empathy_mic_menu_update (self);
252 empathy_mic_menu_microphone_removed_cb (EmpathyGstAudioSrc *audio,
254 EmpathyMicMenu *self)
256 EmpathyMicMenuPrivate *priv = self->priv;
259 for (l = priv->microphones->head; l != NULL; l = l->next)
261 GtkRadioAction *action = l->data;
264 g_object_get (action, "value", &value, NULL);
266 if (value != (gint) source_idx)
272 g_signal_handlers_disconnect_by_func (action,
273 G_CALLBACK (empathy_mic_menu_activate_cb), self);
275 gtk_action_group_remove_action (priv->action_group, GTK_ACTION (action));
276 g_queue_remove (priv->microphones, action);
280 empathy_mic_menu_update (self);
284 empathy_mic_menu_get_microphones_cb (GObject *source_object,
285 GAsyncResult *result,
288 EmpathyGstAudioSrc *audio = EMPATHY_GST_AUDIO_SRC (source_object);
289 EmpathyMicMenu *self = user_data;
290 GError *error = NULL;
291 const GList *mics = NULL;
293 mics = empathy_audio_src_get_microphones_finish (audio, result, &error);
297 g_debug ("Failed to get microphone list: %s", error->message);
298 g_clear_error (&error);
302 for (; mics != NULL; mics = mics->next)
304 EmpathyAudioSrcMicrophone *mic = mics->data;
306 empathy_mic_menu_add_microphone (self, mic->name,
307 mic->description, mic->index);
310 empathy_mic_menu_update (self);
314 empathy_mic_menu_constructed (GObject *obj)
316 EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
317 EmpathyMicMenuPrivate *priv = self->priv;
318 GtkUIManager *ui_manager;
319 EmpathyGstAudioSrc *audio;
321 g_assert (EMPATHY_IS_CALL_WINDOW (priv->window));
323 ui_manager = empathy_call_window_get_ui_manager (priv->window);
324 audio = empathy_call_window_get_audio_src (priv->window);
326 g_assert (GTK_IS_UI_MANAGER (ui_manager));
327 g_assert (EMPATHY_IS_GST_AUDIO_SRC (audio));
329 /* Okay let's go go go. */
331 priv->action_group = gtk_action_group_new ("EmpathyMicMenu");
332 gtk_ui_manager_insert_action_group (ui_manager, priv->action_group, -1);
333 /* the UI manager now owns this */
334 g_object_unref (priv->action_group);
336 priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
337 "name", "EmpathyMicMenuAnchorAction",
339 gtk_action_group_add_action (priv->action_group, priv->anchor_action);
340 g_object_unref (priv->anchor_action);
342 tp_g_signal_connect_object (audio, "notify::microphone",
343 G_CALLBACK (empathy_mic_menu_notify_microphone_cb), self, 0);
344 tp_g_signal_connect_object (audio, "microphone-added",
345 G_CALLBACK (empathy_mic_menu_microphone_added_cb), self, 0);
346 tp_g_signal_connect_object (audio, "microphone-removed",
347 G_CALLBACK (empathy_mic_menu_microphone_removed_cb), self, 0);
349 priv->microphones = g_queue_new ();
351 empathy_audio_src_get_microphones_async (audio,
352 empathy_mic_menu_get_microphones_cb, self);
356 empathy_mic_menu_dispose (GObject *obj)
358 EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
359 EmpathyMicMenuPrivate *priv = self->priv;
361 if (priv->microphones != NULL)
362 g_queue_free (priv->microphones);
363 priv->microphones = NULL;
365 G_OBJECT_CLASS (empathy_mic_menu_parent_class)->dispose (obj);
369 empathy_mic_menu_class_init (EmpathyMicMenuClass *klass)
371 GObjectClass *object_class = G_OBJECT_CLASS (klass);
373 object_class->set_property = empathy_mic_menu_set_property;
374 object_class->get_property = empathy_mic_menu_get_property;
375 object_class->constructed = empathy_mic_menu_constructed;
376 object_class->dispose = empathy_mic_menu_dispose;
378 g_object_class_install_property (object_class, PROP_WINDOW,
379 g_param_spec_object ("window", "window", "window",
380 EMPATHY_TYPE_CALL_WINDOW,
381 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
383 g_type_class_add_private (object_class, sizeof (EmpathyMicMenuPrivate));
387 empathy_mic_menu_new (EmpathyCallWindow *window)
389 return g_object_new (EMPATHY_TYPE_MIC_MENU,