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"
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. */
49 /* TRUE if we're in _update and so calling _set_active. */
52 /* Queue of GtkRadioActions. */
55 EmpathyMicMonitor
*mic_monitor
;
58 G_DEFINE_TYPE (EmpathyMicMenu
, empathy_mic_menu
, G_TYPE_OBJECT
);
60 #define MONITOR_KEY "empathy-mic-menu-is-monitor"
67 static void empathy_mic_menu_update (EmpathyMicMenu
*self
);
70 empathy_mic_menu_init (EmpathyMicMenu
*self
)
72 EmpathyMicMenuPrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
73 EMPATHY_TYPE_MIC_MENU
, EmpathyMicMenuPrivate
);
79 empathy_mic_menu_set_property (GObject
*object
,
84 EmpathyMicMenu
*self
= EMPATHY_MIC_MENU (object
);
85 EmpathyMicMenuPrivate
*priv
= self
->priv
;
90 priv
->window
= g_value_get_object (value
);
93 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
98 empathy_mic_menu_get_property (GObject
*object
,
103 EmpathyMicMenu
*self
= EMPATHY_MIC_MENU (object
);
104 EmpathyMicMenuPrivate
*priv
= self
->priv
;
109 g_value_set_object (value
, priv
->window
);
112 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
117 empathy_mic_menu_clean (EmpathyMicMenu
*self
)
119 EmpathyMicMenuPrivate
*priv
= self
->priv
;
120 GtkUIManager
*ui_manager
;
122 if (priv
->ui_id
== 0)
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
);
133 empathy_mic_menu_change_mic_cb (GObject
*source_object
,
134 GAsyncResult
*result
,
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
);
157 empathy_mic_menu_activate_cb (GtkToggleAction
*action
,
158 EmpathyMicMenu
*self
)
160 EmpathyMicMenuPrivate
*priv
= self
->priv
;
161 EmpathyGstAudioSrc
*audio
;
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
);
176 empathy_mic_menu_update (EmpathyMicMenu
*self
)
178 EmpathyMicMenuPrivate
*priv
= self
->priv
;
180 GtkUIManager
*ui_manager
;
181 EmpathyGstAudioSrc
*audio
;
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
));
199 g_object_get (action
, "value", &value
, NULL
);
201 active
= (value
== (gint
) current_mic
);
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
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
);
229 empathy_mic_menu_add_microphone (EmpathyMicMenu
*self
,
231 const gchar
*description
,
235 EmpathyMicMenuPrivate
*priv
= self
->priv
;
236 GtkRadioAction
*action
;
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. */
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
);
261 empathy_mic_menu_notify_microphone_cb (EmpathyGstAudioSrc
*audio
,
263 EmpathyMicMenu
*self
)
265 empathy_mic_menu_update (self
);
269 empathy_mic_menu_microphone_added_cb (EmpathyMicMonitor
*monitor
,
272 const gchar
*description
,
274 EmpathyMicMenu
*self
)
276 empathy_mic_menu_add_microphone (self
, name
, description
,
277 source_idx
, is_monitor
);
279 empathy_mic_menu_update (self
);
283 empathy_mic_menu_microphone_removed_cb (EmpathyMicMonitor
*monitor
,
285 EmpathyMicMenu
*self
)
287 EmpathyMicMenuPrivate
*priv
= self
->priv
;
290 for (l
= priv
->microphones
->head
; l
!= NULL
; l
= l
->next
)
292 GtkRadioAction
*action
= l
->data
;
295 g_object_get (action
, "value", &value
, NULL
);
297 if (value
!= (gint
) source_idx
)
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
);
311 empathy_mic_menu_update (self
);
315 empathy_mic_menu_list_microphones_cb (GObject
*source_object
,
316 GAsyncResult
*result
,
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
);
328 DEBUG ("Failed to get microphone list: %s", error
->message
);
329 g_clear_error (&error
);
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
);
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",
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
))
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
);
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
);
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
));
428 empathy_mic_menu_new (EmpathyCallWindow
*window
)
430 return g_object_new (EMPATHY_TYPE_MIC_MENU
,