1 
2 /*
3 Copyright 2011 Canonical Ltd.
4 
5 Authors:
6     Conor Curran <conor.curran@canonical.com>
7 
8 This program is free software: you can redistribute it and/or modify it
9 under the terms of the GNU General Public License version 3, as published
10 by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranties of
14 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15 PURPOSE.  See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License along
18 with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <glib/gi18n.h>
26 #include <math.h>
27 #include <glib.h>
28 #include "voip-input-widget.h"
29 #include "common-defs.h"
30 #include <libido/idoscalemenuitem.h>
31 
32 typedef struct _VoipInputWidgetPrivate VoipInputWidgetPrivate;
33 
34 struct _VoipInputWidgetPrivate
35 {
36   DbusmenuMenuitem* twin_item;
37   GtkWidget* ido_voip_input_slider;
38   gboolean grabbed;
39 };
40 
41 #define VOIP_INPUT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), VOIP_INPUT_WIDGET_TYPE, VoipInputWidgetPrivate))
42 
43 /* Prototypes */
44 static void voip_input_widget_class_init (VoipInputWidgetClass *klass);
45 static void voip_input_widget_init       (VoipInputWidget *self);
46 static void voip_input_widget_dispose    (GObject *object);
47 static void voip_input_widget_finalize   (GObject *object);
48 static void voip_input_widget_set_twin_item( VoipInputWidget* self,
49                                         DbusmenuMenuitem* twin_item);
50 static void voip_input_widget_property_update( DbusmenuMenuitem* item, gchar* property,
51                                            GVariant* value, gpointer userdata );
52 
53 static gboolean voip_input_widget_change_value_cb (GtkRange     *range,
54                                               GtkScrollType scroll,
55                                               gdouble       value,
56                                               gpointer      user_data);
57 static gboolean voip_input_widget_value_changed_cb(GtkRange *range, gpointer user_data);
58 static void voip_input_widget_slider_grabbed(GtkWidget *widget, gpointer user_data);
59 static void voip_input_widget_slider_released(GtkWidget *widget, gpointer user_data);
60 static void voip_input_widget_parent_changed (GtkWidget *widget, gpointer user_data);
61 
62 G_DEFINE_TYPE (VoipInputWidget, voip_input_widget, G_TYPE_OBJECT);
63 
64 
65 static void
voip_input_widget_class_init(VoipInputWidgetClass * klass)66 voip_input_widget_class_init (VoipInputWidgetClass *klass)
67 {
68   GObjectClass      *gobject_class = G_OBJECT_CLASS (klass);
69 
70   g_type_class_add_private (klass, sizeof (VoipInputWidgetPrivate));
71 
72   gobject_class->dispose = voip_input_widget_dispose;
73   gobject_class->finalize = voip_input_widget_finalize;
74 }
75 
76 static void
voip_input_widget_init(VoipInputWidget * self)77 voip_input_widget_init (VoipInputWidget *self)
78 {
79   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
80 
81   priv->ido_voip_input_slider = ido_scale_menu_item_new_with_range ("VOLUME", IDO_RANGE_STYLE_DEFAULT,  0, 0, 100, 1);
82   g_object_ref (priv->ido_voip_input_slider);
83   ido_scale_menu_item_set_primary_label (IDO_SCALE_MENU_ITEM(priv->ido_voip_input_slider), "VOIP");
84 
85   ido_scale_menu_item_set_style (IDO_SCALE_MENU_ITEM (priv->ido_voip_input_slider), IDO_SCALE_MENU_ITEM_STYLE_IMAGE);
86   g_object_set(priv->ido_voip_input_slider, "reverse-scroll-events", TRUE, NULL);
87 
88   g_signal_connect (priv->ido_voip_input_slider,
89                     "notify::parent", G_CALLBACK (voip_input_widget_parent_changed),
90                     NULL);
91 
92   GtkWidget* voip_input_widget = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
93 
94   g_signal_connect(voip_input_widget, "change-value", G_CALLBACK(voip_input_widget_change_value_cb), self);
95   g_signal_connect(voip_input_widget, "value-changed", G_CALLBACK(voip_input_widget_value_changed_cb), self);
96   g_signal_connect(priv->ido_voip_input_slider, "slider-grabbed", G_CALLBACK(voip_input_widget_slider_grabbed), self);
97   g_signal_connect(priv->ido_voip_input_slider, "slider-released", G_CALLBACK(voip_input_widget_slider_released), self);
98 
99   GtkWidget* primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)priv->ido_voip_input_slider);
100   GIcon * primary_gicon = g_themed_icon_new_with_default_fallbacks("audio-input-microphone-low-zero-panel");
101   gtk_image_set_from_gicon(GTK_IMAGE(primary_image), primary_gicon, GTK_ICON_SIZE_MENU);
102   g_object_unref(primary_gicon);
103 
104   GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)priv->ido_voip_input_slider);
105   GIcon * secondary_gicon = g_themed_icon_new_with_default_fallbacks("audio-input-microphone-high-panel");
106   gtk_image_set_from_gicon(GTK_IMAGE(secondary_image), secondary_gicon, GTK_ICON_SIZE_MENU);
107   g_object_unref(secondary_gicon);
108 
109   GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (voip_input_widget));
110   gtk_adjustment_set_step_increment(adj, 4);
111 }
112 
113 static void
voip_input_widget_dispose(GObject * object)114 voip_input_widget_dispose (GObject *object)
115 {
116   G_OBJECT_CLASS (voip_input_widget_parent_class)->dispose (object);
117 }
118 
119 static void
voip_input_widget_finalize(GObject * object)120 voip_input_widget_finalize (GObject *object)
121 {
122 	/// we need to unref what we ref'ed
123 	VoipInputWidget *self = VOIP_INPUT_WIDGET (object);
124 	VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
125 	g_object_unref (priv->twin_item);
126 	g_object_unref (priv->ido_voip_input_slider);
127   G_OBJECT_CLASS (voip_input_widget_parent_class)->finalize (object);
128 }
129 
130 static void
voip_input_widget_property_update(DbusmenuMenuitem * item,gchar * property,GVariant * value,gpointer userdata)131 voip_input_widget_property_update (DbusmenuMenuitem* item, gchar* property,
132                                    GVariant* value, gpointer userdata)
133 {
134   g_return_if_fail (IS_VOIP_INPUT_WIDGET (userdata));
135   VoipInputWidget* mitem = VOIP_INPUT_WIDGET(userdata);
136   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
137   if(g_ascii_strcasecmp(DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL, property) == 0){
138     g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
139     if (priv->grabbed == FALSE){
140       GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
141       GtkRange *range = (GtkRange*)slider;
142       gdouble update = g_variant_get_double (value);
143       //g_debug("volume-widget - update level with value %f", update);
144       gtk_range_set_value(range, update);
145     }
146   }
147   if(g_ascii_strcasecmp(DBUSMENU_VOIP_INPUT_MENUITEM_MUTE, property) == 0){
148     if(priv->grabbed == FALSE){
149       g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_INT32));
150       GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
151       GtkRange *range = (GtkRange*)slider;
152       gint update = g_variant_get_int32 (value);
153       gdouble level;
154       if (update == 1){
155         level = 0;
156       }
157       else{
158         GVariant* variant = dbusmenu_menuitem_property_get_variant (priv->twin_item,
159                                                                     DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL);
160         g_return_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_DOUBLE));
161         level = g_variant_get_double (variant);
162       }
163       gtk_range_set_value(range, level);
164 
165       g_debug ("voip-item-widget - update mute with value %i", update);
166     }
167   }
168 }
169 
170 static void
voip_input_widget_set_twin_item(VoipInputWidget * self,DbusmenuMenuitem * twin_item)171 voip_input_widget_set_twin_item (VoipInputWidget* self,
172                                  DbusmenuMenuitem* twin_item)
173 {
174   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
175   priv->twin_item = twin_item;
176   g_object_ref(priv->twin_item);
177   g_signal_connect(G_OBJECT(twin_item), "property-changed",
178                    G_CALLBACK(voip_input_widget_property_update), self);
179   gdouble initial_level = g_variant_get_double (dbusmenu_menuitem_property_get_variant(twin_item,
180                                                 DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL));
181   //g_debug("voip_input_widget_set_twin_item initial level = %f", initial_level);
182   GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
183   GtkRange *range = (GtkRange*)slider;
184   gtk_range_set_value(range, initial_level);
185 
186   gint mute = g_variant_get_int32 (dbusmenu_menuitem_property_get_variant (priv->twin_item,
187                                                                            DBUSMENU_VOIP_INPUT_MENUITEM_MUTE));
188   if (mute == 1){
189     gtk_range_set_value (range, 0.0);
190   }
191 }
192 
193 static gboolean
voip_input_widget_change_value_cb(GtkRange * range,GtkScrollType scroll,gdouble new_value,gpointer user_data)194 voip_input_widget_change_value_cb (GtkRange     *range,
195                                    GtkScrollType scroll,
196                                    gdouble       new_value,
197                                    gpointer      user_data)
198 {
199   g_return_val_if_fail (IS_VOIP_INPUT_WIDGET (user_data), FALSE);
200   VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
201   voip_input_widget_update(mitem, new_value);
202   return FALSE;
203 }
204 
205 
206 /**
207  * We only want this callback to catch mouse icon press events which set the
208  * slider to 0 or 100. Ignore all other events including the new Mute behaviour
209  * (slider to go to 0 on mute without setting the level to 0 and return to
210  * previous level on unmute)
211  **/
212 static gboolean
voip_input_widget_value_changed_cb(GtkRange * range,gpointer user_data)213 voip_input_widget_value_changed_cb(GtkRange *range, gpointer user_data)
214 {
215   g_return_val_if_fail (IS_VOIP_INPUT_WIDGET (user_data), FALSE);
216   VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
217   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
218   GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider);
219   gdouble current_value =  CLAMP(gtk_range_get_value(GTK_RANGE(slider)), 0, 100);
220 
221   gint mute = g_variant_get_int32 (dbusmenu_menuitem_property_get_variant (priv->twin_item,
222                                                                            DBUSMENU_VOIP_INPUT_MENUITEM_MUTE));
223   if ((current_value == 0 && mute != 1) || current_value == 100 ){
224     voip_input_widget_update(mitem, current_value);
225   }
226   return FALSE;
227 }
228 
229 void
voip_input_widget_update(VoipInputWidget * self,gdouble update)230 voip_input_widget_update(VoipInputWidget* self, gdouble update)
231 {
232   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
233   gdouble clamped = CLAMP(update, 0, 100);
234   GVariant* new_volume = g_variant_new_double(clamped);
235   dbusmenu_menuitem_handle_event (priv->twin_item, "update", new_volume, 0);
236 }
237 
238 GtkWidget*
voip_input_widget_get_ido_slider(VoipInputWidget * self)239 voip_input_widget_get_ido_slider(VoipInputWidget* self)
240 {
241   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self);
242   return priv->ido_voip_input_slider;
243 }
244 
245 static void
voip_input_widget_parent_changed(GtkWidget * widget,gpointer user_data)246 voip_input_widget_parent_changed (GtkWidget *widget,
247                               gpointer   user_data)
248 {
249   gtk_widget_set_size_request (widget, 200, -1);
250   //g_debug("voip_input_widget_parent_changed");
251 }
252 
253 static void
voip_input_widget_slider_grabbed(GtkWidget * widget,gpointer user_data)254 voip_input_widget_slider_grabbed(GtkWidget *widget, gpointer user_data)
255 {
256   VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
257   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
258   priv->grabbed = TRUE;
259 }
260 
261 static void
voip_input_widget_slider_released(GtkWidget * widget,gpointer user_data)262 voip_input_widget_slider_released(GtkWidget *widget, gpointer user_data)
263 {
264   VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data);
265   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
266   priv->grabbed = FALSE;
267 }
268 
269 void
voip_input_widget_tidy_up(GtkWidget * widget)270 voip_input_widget_tidy_up (GtkWidget *widget)
271 {
272   VoipInputWidget* mitem = VOIP_INPUT_WIDGET(widget);
273   VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem);
274   gtk_widget_destroy (priv->ido_voip_input_slider);
275 }
276 
277 /**
278  * voip_input_widget_new:
279  * @returns: a new #VoipInputWidget.
280  **/
281 GtkWidget*
voip_input_widget_new(DbusmenuMenuitem * item)282 voip_input_widget_new(DbusmenuMenuitem *item)
283 {
284   GtkWidget* widget = g_object_new(VOIP_INPUT_WIDGET_TYPE, NULL);
285   voip_input_widget_set_twin_item((VoipInputWidget*)widget, item);
286   return widget;
287 }
288 
289 
290