1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2007 Red Hat, Inc.
3  *
4  * Authors:
5  * - Bastien Nocera <bnocera@redhat.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /*
22  * Modified by the GTK+ Team and others 2007.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26  */
27 
28 #include "config.h"
29 
30 #include "gtkvolumebutton.h"
31 
32 #include "gtkadjustment.h"
33 #include "gtkintl.h"
34 #include "gtktooltip.h"
35 
36 
37 /**
38  * SECTION:gtkvolumebutton
39  * @Short_description: A button which pops up a volume control
40  * @Title: GtkVolumeButton
41  *
42  * #GtkVolumeButton is a subclass of #GtkScaleButton that has
43  * been tailored for use as a volume control widget with suitable
44  * icons, tooltips and accessible labels.
45  */
46 
47 #define EPSILON (1e-10)
48 
49 static const gchar * const icons[] =
50 {
51   "audio-volume-muted",
52   "audio-volume-high",
53   "audio-volume-low",
54   "audio-volume-medium",
55   NULL
56 };
57 
58 static const gchar * const icons_symbolic[] =
59 {
60   "audio-volume-muted-symbolic",
61   "audio-volume-high-symbolic",
62   "audio-volume-low-symbolic",
63   "audio-volume-medium-symbolic",
64   NULL
65 };
66 
67 enum
68 {
69   PROP_0,
70   PROP_SYMBOLIC
71 };
72 
73 static gboolean	cb_query_tooltip (GtkWidget       *button,
74                                   gint             x,
75                                   gint             y,
76                                   gboolean         keyboard_mode,
77                                   GtkTooltip      *tooltip,
78                                   gpointer         user_data);
79 static void	cb_value_changed (GtkVolumeButton *button,
80                                   gdouble          value,
81                                   gpointer         user_data);
82 
G_DEFINE_TYPE(GtkVolumeButton,gtk_volume_button,GTK_TYPE_SCALE_BUTTON)83 G_DEFINE_TYPE (GtkVolumeButton, gtk_volume_button, GTK_TYPE_SCALE_BUTTON)
84 
85 static gboolean
86 get_symbolic (GtkScaleButton *button)
87 {
88   gchar **icon_list;
89   gboolean ret;
90 
91   g_object_get (button, "icons", &icon_list, NULL);
92   if (icon_list != NULL &&
93       icon_list[0] != NULL &&
94       g_str_equal (icon_list[0], icons_symbolic[0]))
95     ret = TRUE;
96   else
97     ret = FALSE;
98   g_strfreev (icon_list);
99 
100   return ret;
101 }
102 
103 static void
gtk_volume_button_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)104 gtk_volume_button_set_property (GObject       *object,
105 				guint          prop_id,
106 				const GValue  *value,
107 				GParamSpec    *pspec)
108 {
109   GtkScaleButton *button = GTK_SCALE_BUTTON (object);
110 
111   switch (prop_id)
112     {
113     case PROP_SYMBOLIC:
114       if (get_symbolic (button) != g_value_get_boolean (value))
115         {
116           if (g_value_get_boolean (value))
117             gtk_scale_button_set_icons (button, (const char **) icons_symbolic);
118           else
119 	    gtk_scale_button_set_icons (button, (const char **) icons);
120           g_object_notify_by_pspec (object, pspec);
121         }
122       break;
123     default:
124       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
125       break;
126     }
127 }
128 
129 static void
gtk_volume_button_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)130 gtk_volume_button_get_property (GObject     *object,
131 			        guint        prop_id,
132 			        GValue      *value,
133 			        GParamSpec  *pspec)
134 {
135   switch (prop_id)
136     {
137     case PROP_SYMBOLIC:
138       g_value_set_boolean (value, get_symbolic (GTK_SCALE_BUTTON (object)));
139       break;
140     default:
141       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
142       break;
143     }
144 }
145 
146 static void
gtk_volume_button_class_init(GtkVolumeButtonClass * klass)147 gtk_volume_button_class_init (GtkVolumeButtonClass *klass)
148 {
149   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
150   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
151 
152   gobject_class->set_property = gtk_volume_button_set_property;
153   gobject_class->get_property = gtk_volume_button_get_property;
154 
155   /**
156    * GtkVolumeButton:use-symbolic:
157    *
158    * Whether to use symbolic icons as the icons. Note that
159    * if the symbolic icons are not available in your installed
160    * theme, then the normal (potentially colorful) icons will
161    * be used.
162    *
163    * Since: 3.0
164    */
165   g_object_class_install_property (gobject_class,
166                                    PROP_SYMBOLIC,
167                                    g_param_spec_boolean ("use-symbolic",
168                                                          P_("Use symbolic icons"),
169                                                          P_("Whether to use symbolic icons"),
170                                                          TRUE,
171                                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY));
172 
173   /* Bind class to template
174    */
175   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkvolumebutton.ui");
176   gtk_widget_class_bind_template_callback (widget_class, cb_query_tooltip);
177   gtk_widget_class_bind_template_callback (widget_class, cb_value_changed);
178 }
179 
180 static void
gtk_volume_button_init(GtkVolumeButton * button)181 gtk_volume_button_init (GtkVolumeButton *button)
182 {
183   GtkWidget *widget = GTK_WIDGET (button);
184 
185   gtk_widget_init_template (widget);
186 
187   /* The atk action description is not supported by GtkBuilder */
188   atk_action_set_description (ATK_ACTION (gtk_widget_get_accessible (GTK_WIDGET (widget))),
189 			      1, _("Adjusts the volume"));
190 }
191 
192 /**
193  * gtk_volume_button_new:
194  *
195  * Creates a #GtkVolumeButton, with a range between 0.0 and 1.0, with
196  * a stepping of 0.02. Volume values can be obtained and modified using
197  * the functions from #GtkScaleButton.
198  *
199  * Returns: a new #GtkVolumeButton
200  *
201  * Since: 2.12
202  */
203 GtkWidget *
gtk_volume_button_new(void)204 gtk_volume_button_new (void)
205 {
206   GObject *button;
207   button = g_object_new (GTK_TYPE_VOLUME_BUTTON, NULL);
208   return GTK_WIDGET (button);
209 }
210 
211 static gboolean
cb_query_tooltip(GtkWidget * button,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)212 cb_query_tooltip (GtkWidget  *button,
213 		  gint        x,
214 		  gint        y,
215 		  gboolean    keyboard_mode,
216 		  GtkTooltip *tooltip,
217 		  gpointer    user_data)
218 {
219   GtkScaleButton *scale_button = GTK_SCALE_BUTTON (button);
220   GtkAdjustment *adjustment;
221   gdouble val;
222   char *str;
223   AtkImage *image;
224 
225   image = ATK_IMAGE (gtk_widget_get_accessible (button));
226 
227   adjustment = gtk_scale_button_get_adjustment (scale_button);
228   val = gtk_scale_button_get_value (scale_button);
229 
230   if (val < (gtk_adjustment_get_lower (adjustment) + EPSILON))
231     {
232       str = g_strdup (_("Muted"));
233     }
234   else if (val >= (gtk_adjustment_get_upper (adjustment) - EPSILON))
235     {
236       str = g_strdup (_("Full Volume"));
237     }
238   else
239     {
240       int percent;
241 
242       percent = (int) (100. * val / (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment)) + .5);
243 
244       /* Translators: this is the percentage of the current volume,
245        * as used in the tooltip, eg. "49 %".
246        * Translate the "%d" to "%Id" if you want to use localised digits,
247        * or otherwise translate the "%d" to "%d".
248        */
249       str = g_strdup_printf (C_("volume percentage", "%d %%"), percent);
250     }
251 
252   gtk_tooltip_set_text (tooltip, str);
253   atk_image_set_image_description (image, str);
254   g_free (str);
255 
256   return TRUE;
257 }
258 
259 static void
cb_value_changed(GtkVolumeButton * button,gdouble value,gpointer user_data)260 cb_value_changed (GtkVolumeButton *button, gdouble value, gpointer user_data)
261 {
262   gtk_widget_trigger_tooltip_query (GTK_WIDGET (button));
263 }
264