Add a callback into plugin init struct for panel context menu update.
[lxde/lxpanel.git] / src / plugins / volume / volume.c
CommitLineData
4542c20d 1/**
e5035ac1
JH
2 * Copyright (c) 2006, 2008 LxDE Developers,
3 * see the file AUTHORS for details.
a99ee9e1
JH
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
31b80004
JH
20#include <gtk/gtk.h>
21#include <stdlib.h>
22#include <fcntl.h>
23#include <unistd.h>
24
f277dbb7 25#include <glib/gi18n.h>
31b80004
JH
26#include <gdk-pixbuf/gdk-pixbuf.h>
27
6958db5d 28#include "plugin.h"
31b80004
JH
29
30#include "dbg.h"
31
32#include "volume-impl.h"
33
34#include "volume_xpm.h"
35
36int mixer_fd;
37
e5035ac1
JH
38GtkSpinButton *vol_spin;
39static gdouble vol_before_mute;
40static gdouble curr_volume;
41static GtkWidget *curr_image;
42static gboolean skip_botton1_event;
43
31b80004 44typedef struct {
6958db5d 45 Panel *panel;
4542c20d
HJYP
46 GtkWidget *dlg;
47 GtkTooltips* tooltips;
31b80004
JH
48} volume_t;
49
50static void
6958db5d 51volume_destructor(gpointer user_data)
31b80004 52{
6958db5d 53 volume_t *vol = (volume_t *) user_data;
4542c20d
HJYP
54
55 ENTER;
56 if (vol->dlg)
57 gtk_widget_destroy(vol->dlg);
58 g_object_unref( vol->tooltips );
4542c20d
HJYP
59 if (mixer_fd)
60 close(mixer_fd);
61 g_free(vol);
62 RET();
31b80004
JH
63}
64
6958db5d 65static void update_icon (GtkWidget *p, volume_t *vol)
31b80004 66{
e5035ac1
JH
67 GdkPixbuf *icon;
68 GtkWidget *image;
69 GtkIconTheme* theme;
70 GtkIconInfo* info;
6958db5d
AG
71 int icon_size;
72
73 theme = panel_get_icon_theme(vol->panel);
74 icon_size = panel_get_icon_size(vol->panel);
75
e5035ac1 76 if (curr_volume <= 0) {
6958db5d 77 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-mute", icon_size, 0 );
e5035ac1
JH
78 }
79 else if (curr_volume > 0 && curr_volume <= 50) {
6958db5d 80 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-min", icon_size, 0 );
e5035ac1
JH
81 }
82 else if (curr_volume > 50 && curr_volume <= 75) {
6958db5d 83 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-med", icon_size, 0 );
e5035ac1 84 }
6958db5d
AG
85 else /* curr_volume > 75 */ {
86 info = gtk_icon_theme_lookup_icon( theme, "stock_volume-max", icon_size, 0 );
e5035ac1
JH
87 }
88
89 if (info ) {
90 icon = gdk_pixbuf_new_from_file_at_size(
91 gtk_icon_info_get_filename( info ),
6958db5d 92 icon_size, icon_size, NULL );
e5035ac1
JH
93 gtk_icon_info_free( info );
94 }
95 else {
96 icon = gdk_pixbuf_new_from_xpm_data((const char **) volume_xpm);
97 }
98
99
100
101 if (icon) {
102 if (curr_image) {
6958db5d 103 gtk_container_remove(GTK_CONTAINER(p), curr_image);
e5035ac1
JH
104 curr_image = NULL;
105 }
106 image = gtk_image_new_from_pixbuf(icon);
6958db5d 107 gtk_container_add(GTK_CONTAINER(p), image);
e5035ac1
JH
108
109 curr_image = image;
110 }
6958db5d 111 gtk_widget_show_all(p);
e5035ac1 112 return;
31b80004
JH
113}
114
6958db5d 115static void on_volume_focus (GtkWidget* dlg, GdkEventFocus *event, GtkWidget *p)
31b80004 116{
6958db5d
AG
117 volume_t *vol = lxpanel_plugin_get_data(p);
118
e5035ac1
JH
119 if (! vol_spin) return;
120 GtkAdjustment *vol_adjustment = gtk_spin_button_get_adjustment (vol_spin);
121 if (! vol_adjustment) return;
122 curr_volume = gtk_adjustment_get_value (vol_adjustment);
123
6958db5d 124 update_icon(p, vol);
e5035ac1
JH
125
126 /* FIXME: use smarter method */
127 gtk_widget_destroy( dlg );
128 vol->dlg = NULL;
129}
31b80004 130
6958db5d 131static void on_mouse_scroll (GtkWidget* widget, GdkEventScroll* evt, volume_t *vol)
e5035ac1 132{
e5035ac1 133 if ( ! vol->dlg ) {
cecb0eaa 134
e5035ac1 135 vol->dlg = create_volume_window();
6958db5d
AG
136 g_signal_connect( vol->dlg, "delete-event",
137 G_CALLBACK(on_volume_focus), widget );
cecb0eaa 138
e5035ac1
JH
139 }
140 else {
141 if (! vol_spin) return;
142 GtkAdjustment *vol_adjustment =
143 gtk_spin_button_get_adjustment (vol_spin);
144 if (! vol_adjustment) return;
145
146 curr_volume = gtk_adjustment_get_value (vol_adjustment);
147
148 if (evt->direction == GDK_SCROLL_UP) {
149 curr_volume += 2;
150 }
151 else /*if (evt->direction == GDK_SCROLL_DOWN)*/ {
152 curr_volume -= 2;
153 }
6958db5d 154 update_icon(widget, vol);
e5035ac1
JH
155 gtk_adjustment_set_value (vol_adjustment, curr_volume);
156 gtk_spin_button_set_adjustment(vol_spin, vol_adjustment);
157 skip_botton1_event = TRUE;
158 }
159}
160
6958db5d 161static gboolean on_button_press (GtkWidget* widget, GdkEventButton* evt, Panel* p)
e5035ac1 162{
6958db5d 163 volume_t *vol = lxpanel_plugin_get_data(widget);
e5035ac1
JH
164
165 /* for scroll correction */
166 if (skip_botton1_event) {
167 gtk_widget_destroy(vol->dlg);
168 vol->dlg = NULL;
169 skip_botton1_event = FALSE;
170 }
171
172 switch ( evt->button ) {
173 case 1: { /* Left click */
174 if ( ! vol->dlg ) {
175 vol->dlg = create_volume_window();
176
177 /* setting background to default */
6958db5d 178 gtk_widget_set_style(vol->dlg, panel_get_defstyle(p));
e5035ac1
JH
179
180 g_signal_connect( vol->dlg, "focus-out-event",
6958db5d 181 G_CALLBACK(on_volume_focus), widget );
e5035ac1
JH
182
183 gtk_window_present( GTK_WINDOW(vol->dlg) );
184 }
185 else {
186 /* update icon */
68f80334 187 if (! vol_spin) return FALSE;
e5035ac1
JH
188 GtkAdjustment *vol_adjustment =
189 gtk_spin_button_get_adjustment (vol_spin);
68f80334 190 if (! vol_adjustment) return FALSE;
e5035ac1 191 curr_volume = gtk_adjustment_get_value (vol_adjustment);
6958db5d 192 update_icon(widget, vol);
e5035ac1
JH
193
194 gtk_widget_destroy(vol->dlg);
195 vol->dlg = NULL;
196 }
197 break;
198 }
199
200 case 3: { /* right button */
6958db5d 201 lxpanel_plugin_button_press_event(widget, evt, p);
e5035ac1
JH
202 return TRUE;
203 }
204
205 case 2: { /* middle mouse button */
206 if ( ! vol->dlg ) {
207 vol->dlg = create_volume_window();
208 }
209
68f80334 210 if (! vol_spin) return FALSE;
e5035ac1
JH
211 GtkAdjustment *vol_adjustment =
212 gtk_spin_button_get_adjustment (vol_spin);
68f80334 213 if (! vol_adjustment) return FALSE;
e5035ac1
JH
214
215 curr_volume = gtk_adjustment_get_value (vol_adjustment);
216
217 if (curr_volume > 0) {
218 /* turning to mute */
219 vol_before_mute = curr_volume;
220 curr_volume = 0;
221 }
222 else {
223 curr_volume = vol_before_mute;
224 }
225
226 gtk_adjustment_set_value (vol_adjustment, curr_volume);
227 gtk_spin_button_set_adjustment(vol_spin, vol_adjustment);
228
6958db5d 229 update_icon(widget, vol);
e5035ac1
JH
230
231 gtk_widget_destroy( vol->dlg );
232 vol->dlg = NULL;
233 break;
234 }
235 default: /* never here */
236 break;
237 }
238 return FALSE;
31b80004
JH
239}
240
6958db5d 241static GtkWidget *volume_constructor(Panel *panel, config_setting_t *settings)
31b80004 242{
4542c20d 243 volume_t *vol;
6958db5d
AG
244 GtkWidget *p;
245 GtkAdjustment *vol_adjustment;
246
e5035ac1
JH
247 vol_before_mute = 1;
248 curr_volume = 0;
249 curr_image = NULL;
250 skip_botton1_event = FALSE;
4542c20d
HJYP
251
252 ENTER;
4542c20d
HJYP
253 /* check if OSS mixer device could be open */
254 mixer_fd = open ("/dev/mixer", O_RDWR, 0);
255 if (mixer_fd < 0) {
6958db5d
AG
256 RET(NULL);
257 }
258 /* try to obtain current volume */
259 p = create_volume_window(); /* use pointer */
260 if (! vol_spin)
261 goto _error;
262 vol_adjustment = gtk_spin_button_get_adjustment (vol_spin);
263 if (! vol_adjustment)
264 {
265_error:
266 gtk_widget_destroy(p);
267 RET(NULL);
4542c20d 268 }
e5035ac1 269 curr_volume = gtk_adjustment_get_value (vol_adjustment);
6958db5d
AG
270
271 vol = g_new0(volume_t, 1);
272 vol->dlg = p; /* it was reused */
273
274 p = gtk_event_box_new();
275 lxpanel_plugin_set_data(p, vol, volume_destructor);
276 vol->panel = panel;
277
278 gtk_widget_add_events(p, GDK_BUTTON_PRESS_MASK);
279 g_signal_connect(p, "scroll-event", G_CALLBACK(on_mouse_scroll), vol);
280 gtk_widget_set_size_request(p, panel_get_icon_size(panel), panel_get_icon_size(panel));
281
282 update_icon(p, vol);
283 gtk_widget_destroy( vol->dlg );
284 vol->dlg = NULL;
4542c20d
HJYP
285
286 vol->tooltips = gtk_tooltips_new ();
3806c5aa 287 g_object_ref_sink( vol->tooltips );
3806c5aa 288
4542c20d 289 /* FIXME: display current level in tooltip. ex: "Volume Control: 80%" */
6958db5d 290 gtk_tooltips_set_tip (vol->tooltips, p, _("Volume control"), NULL);
31b80004 291
6958db5d 292 RET(p);
31b80004
JH
293}
294
6958db5d 295FM_DEFINE_MODULE(lxpanel_gtk, volume)
31b80004 296
6958db5d 297LXPanelPluginInit fm_module_init_lxpanel_gtk = {
3c3e9c9e 298 .name = N_("Volume Control"),
3c3e9c9e
HG
299 .description = "Display and control volume",
300
6958db5d
AG
301 .new_instance = volume_constructor,
302 .button_press_event = on_button_press
31b80004 303};