1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ |
---|
2 | |
---|
3 | /* |
---|
4 | * Mixer (volume control) applet. |
---|
5 | * |
---|
6 | * (C) Copyright 2001, Richard Hult |
---|
7 | * |
---|
8 | * Author: Richard Hult <richard@imendio.com> |
---|
9 | * |
---|
10 | * |
---|
11 | * Loosely based on the mixer applet: |
---|
12 | * |
---|
13 | * GNOME audio mixer module |
---|
14 | * (C) 1998 The Free Software Foundation |
---|
15 | * |
---|
16 | * Author: Michael Fulbright <msf@redhat.com>: |
---|
17 | * |
---|
18 | * Based on: |
---|
19 | * |
---|
20 | * GNOME time/date display module. |
---|
21 | * (C) 1997 The Free Software Foundation |
---|
22 | * |
---|
23 | * Authors: Miguel de Icaza |
---|
24 | * Federico Mena |
---|
25 | * |
---|
26 | */ |
---|
27 | |
---|
28 | /* |
---|
29 | * Contact Dave Larson <davlarso@acm.org> for questions of |
---|
30 | * bugs concerning the Solaris audio api code. |
---|
31 | * |
---|
32 | */ |
---|
33 | |
---|
34 | #include <config.h> |
---|
35 | |
---|
36 | #include <math.h> |
---|
37 | #include <stdio.h> |
---|
38 | #include <sys/stat.h> |
---|
39 | #include <sys/ioctl.h> |
---|
40 | #include <unistd.h> |
---|
41 | #include <fcntl.h> |
---|
42 | #include <string.h> |
---|
43 | #include <errno.h> |
---|
44 | #include <stdlib.h> |
---|
45 | |
---|
46 | #include <gtk/gtk.h> |
---|
47 | #include <gdk/gdkkeysyms.h> |
---|
48 | #include <libgnome/libgnome.h> |
---|
49 | #include <libgnomeui/gnome-help.h> |
---|
50 | #include <libgnomeui/gnome-about.h> |
---|
51 | #include <libgnomeui/gnome-window-icon.h> |
---|
52 | #include <gconf/gconf-client.h> |
---|
53 | #include <panel-applet-gconf.h> |
---|
54 | |
---|
55 | #ifdef HAVE_LINUX_SOUNDCARD_H |
---|
56 | #include <linux/soundcard.h> |
---|
57 | #define OSS_API |
---|
58 | #elif defined HAVE_MACHINE_SOUNDCARD_H |
---|
59 | #include <machine/soundcard.h> |
---|
60 | #define OSS_API |
---|
61 | #elif defined HAVE_SYS_SOUNDCARD_H |
---|
62 | #include <sys/soundcard.h> |
---|
63 | #define OSS_API |
---|
64 | #elif defined HAVE_SOUNDCARD_H |
---|
65 | #include <soundcard.h> |
---|
66 | #define OSS_API |
---|
67 | #elif defined HAVE_SYS_AUDIOIO_H |
---|
68 | #include <sys/audioio.h> |
---|
69 | #define SUN_API |
---|
70 | #elif defined HAVE_SYS_AUDIO_IO_H |
---|
71 | #include <sys/audio.io.h> |
---|
72 | #define SUN_API |
---|
73 | #elif defined HAVE_SUN_AUDIOIO_H |
---|
74 | #include <sun/audioio.h> |
---|
75 | #define SUN_API |
---|
76 | #elif defined HAVE_DMEDIA_AUDIO_H |
---|
77 | #define IRIX_API |
---|
78 | #include <dmedia/audio.h> |
---|
79 | #elif defined HAVE_SYS_AUDIO_H |
---|
80 | #include <sys/audio.h> |
---|
81 | #define AIX_API |
---|
82 | #elif defined HAVE_GSTREAMER |
---|
83 | #define GSTREAMER_API |
---|
84 | #include <gst/gst.h> |
---|
85 | #include <gst/mixer/mixer.h> |
---|
86 | #include <gst/propertyprobe/propertyprobe.h> |
---|
87 | #else |
---|
88 | #error No soundcard defenition! |
---|
89 | #endif /* SOUNDCARD_H */ |
---|
90 | |
---|
91 | #ifdef OSS_API |
---|
92 | #define VOLUME_MAX 100 |
---|
93 | #define ALLOW_PREFERENCES |
---|
94 | #endif |
---|
95 | #ifdef SUN_API |
---|
96 | #define VOLUME_MAX 255 |
---|
97 | #endif |
---|
98 | #ifdef IRIX_API |
---|
99 | #define VOLUME_MAX 255 |
---|
100 | #endif |
---|
101 | #ifdef AIX_API |
---|
102 | #define VOLUME_MAX 160 |
---|
103 | #endif |
---|
104 | #ifdef GSTREAMER_API |
---|
105 | #define VOLUME_MAX 100 |
---|
106 | #define ALLOW_PREFERENCES |
---|
107 | #endif |
---|
108 | |
---|
109 | #define VOLUME_STOCK_MUTE "volume-mute" |
---|
110 | #define VOLUME_STOCK_ZERO "volume-zero" |
---|
111 | #define VOLUME_STOCK_MIN "volume-min" |
---|
112 | #define VOLUME_STOCK_MED "volume-med" |
---|
113 | #define VOLUME_STOCK_MAX "volume-max" |
---|
114 | |
---|
115 | #define VOLUME_DEFAULT_ICON_SIZE 48 |
---|
116 | static GtkIconSize volume_icon_size = 0; |
---|
117 | static gboolean icons_initialized = FALSE; |
---|
118 | static gboolean device_present = TRUE; |
---|
119 | |
---|
120 | #define IS_PANEL_HORIZONTAL(o) (o == PANEL_APPLET_ORIENT_UP || o == PANEL_APPLET_ORIENT_DOWN) |
---|
121 | |
---|
122 | #ifdef OSS_API |
---|
123 | |
---|
124 | typedef struct { |
---|
125 | gint channel; |
---|
126 | gchar *name; |
---|
127 | } ChannelData; |
---|
128 | |
---|
129 | static gchar *channel_names [] = { |
---|
130 | N_("Main Volume"), N_("Bass"), N_("Treble"), N_("Synth"), |
---|
131 | N_("Pcm"), N_("Speaker"), N_("Line"), |
---|
132 | N_("Microphone"), N_("CD"), N_("Mix"), N_("Pcm2"), |
---|
133 | N_("Recording Level"), N_("Input Gain"), N_("Output Gain"), |
---|
134 | N_("Line1"), N_("Line2"), N_("Line3"), N_("Digital1"), |
---|
135 | N_("Digital2"), N_("Digital3"), |
---|
136 | N_("Phone Input"), N_("Phone Output"), N_("Video"), N_("Radio"), N_("Monitor") |
---|
137 | }; |
---|
138 | #endif |
---|
139 | |
---|
140 | |
---|
141 | #ifdef GSTREAMER_API |
---|
142 | typedef struct { |
---|
143 | gint channel; |
---|
144 | const gchar *name; |
---|
145 | GstMixer *mixer; |
---|
146 | GstMixerTrack *track; |
---|
147 | } ChannelData; |
---|
148 | #endif |
---|
149 | |
---|
150 | typedef struct { |
---|
151 | PanelAppletOrient orientation; |
---|
152 | |
---|
153 | gint timeout; |
---|
154 | gboolean mute; |
---|
155 | gint vol; |
---|
156 | gint vol_before_popup; |
---|
157 | gint channel; |
---|
158 | int mixerchannel; |
---|
159 | GList *channels; |
---|
160 | gchar *device; |
---|
161 | gint panel_size; |
---|
162 | |
---|
163 | GtkAdjustment *adj; |
---|
164 | |
---|
165 | GtkWidget *about_dialog; |
---|
166 | |
---|
167 | GtkWidget *applet; |
---|
168 | GtkWidget *frame; |
---|
169 | GtkWidget *image; |
---|
170 | GtkWidget *prefdialog; |
---|
171 | |
---|
172 | /* The popup window and scale. */ |
---|
173 | GtkWidget *popup; |
---|
174 | GtkWidget *scale; |
---|
175 | |
---|
176 | GtkWidget *error_dialog; |
---|
177 | |
---|
178 | GdkPixbuf *zero; |
---|
179 | GdkPixbuf *min; |
---|
180 | GdkPixbuf *max; |
---|
181 | GdkPixbuf *med; |
---|
182 | GdkPixbuf *muted; |
---|
183 | |
---|
184 | GtkTooltips *tooltips; |
---|
185 | } MixerData; |
---|
186 | |
---|
187 | static void mixer_update_slider (MixerData *data); |
---|
188 | static void mixer_update_image (MixerData *data); |
---|
189 | static void mixer_popup_show (MixerData *data); |
---|
190 | static void mixer_popup_hide (MixerData *data, gboolean revert); |
---|
191 | |
---|
192 | static void mixer_start_gmix_cb (BonoboUIComponent *uic, |
---|
193 | MixerData *data, |
---|
194 | const gchar *verbname); |
---|
195 | static void mixer_ui_component_event (BonoboUIComponent *comp, |
---|
196 | const gchar *path, |
---|
197 | Bonobo_UIComponent_EventType type, |
---|
198 | const gchar *state_string, |
---|
199 | MixerData *data); |
---|
200 | void add_atk_namedesc (GtkWidget *widget, const gchar *name, const gchar *desc); |
---|
201 | |
---|
202 | #ifdef AIX_API |
---|
203 | static gint mstvolfd = -1; /* Used to change the master volume level */ |
---|
204 | #define MAX_MPX_CHAN_LEN 20 |
---|
205 | #define DEFAULT_MST_VOLUME 100 |
---|
206 | #endif |
---|
207 | static gint mixerfd = -1; |
---|
208 | static gchar *run_mixer_cmd = NULL; |
---|
209 | |
---|
210 | static const gchar *access_name = N_("Volume Control"); |
---|
211 | static const gchar *access_name_mute = N_("Volume Control (muted)"); |
---|
212 | static const gchar *access_name_nodevice = N_("No audio device"); |
---|
213 | gboolean gail_loaded = FALSE; |
---|
214 | |
---|
215 | |
---|
216 | /* |
---|
217 | * GStreamer-specific code |
---|
218 | */ |
---|
219 | |
---|
220 | #ifdef GSTREAMER_API |
---|
221 | |
---|
222 | static ChannelData * |
---|
223 | gstreamer_find_mixer_channel (MixerData *data) |
---|
224 | { |
---|
225 | const GList *iter; |
---|
226 | ChannelData *cdata; |
---|
227 | |
---|
228 | for (iter = data->channels; iter != NULL; iter = iter->next) { |
---|
229 | cdata = iter->data; |
---|
230 | if (cdata->channel == data->mixerchannel) |
---|
231 | return cdata; |
---|
232 | } |
---|
233 | |
---|
234 | return NULL; |
---|
235 | } |
---|
236 | |
---|
237 | /* |
---|
238 | Map a volume value from the track's [min_volume, max_volume] |
---|
239 | to [0, VOLUME_MAX]. |
---|
240 | */ |
---|
241 | static gint |
---|
242 | gstreamer_normalize_volume (GstMixerTrack *track, |
---|
243 | int vol) |
---|
244 | { |
---|
245 | double t; |
---|
246 | |
---|
247 | vol = CLAMP (vol, track->min_volume, track->max_volume); |
---|
248 | |
---|
249 | t = (vol - track->min_volume) / |
---|
250 | (double) (track->max_volume - track->min_volume); |
---|
251 | |
---|
252 | return (gint) (VOLUME_MAX * t + 0.5); |
---|
253 | } |
---|
254 | |
---|
255 | /* |
---|
256 | Map a volume value from [0, VOLUME_MAX] to the track's |
---|
257 | [min_volume, max_volume]. |
---|
258 | */ |
---|
259 | static gint |
---|
260 | gstreamer_trackify_volume (GstMixerTrack *track, |
---|
261 | int vol) |
---|
262 | { |
---|
263 | double t; |
---|
264 | |
---|
265 | vol = CLAMP (vol, 0, VOLUME_MAX); |
---|
266 | |
---|
267 | t = vol / (double) VOLUME_MAX; |
---|
268 | |
---|
269 | return (gint) (t * (track->max_volume - track->min_volume) + 0.5 |
---|
270 | + track->min_volume); |
---|
271 | } |
---|
272 | |
---|
273 | static int |
---|
274 | gstreamer_get_volume (MixerData *data) |
---|
275 | { |
---|
276 | ChannelData *cdata; |
---|
277 | gint *volumes; |
---|
278 | gint i; |
---|
279 | double vol = 0; |
---|
280 | |
---|
281 | cdata = gstreamer_find_mixer_channel (data); |
---|
282 | if (cdata == NULL) |
---|
283 | return 0; /* This is sort of lame */ |
---|
284 | |
---|
285 | volumes = g_new0 (gint, cdata->track->num_channels); |
---|
286 | gst_mixer_get_volume (cdata->mixer, cdata->track, volumes); |
---|
287 | for (i = 0; i < cdata->track->num_channels; ++i) { |
---|
288 | vol += volumes[i]; |
---|
289 | } |
---|
290 | g_free (volumes); |
---|
291 | |
---|
292 | vol /= cdata->track->num_channels; |
---|
293 | |
---|
294 | return gstreamer_normalize_volume (cdata->track, (gint) vol); |
---|
295 | } |
---|
296 | |
---|
297 | static void |
---|
298 | gstreamer_set_volume (MixerData *data, gint vol) |
---|
299 | { |
---|
300 | ChannelData *cdata; |
---|
301 | gint *volumes; |
---|
302 | gint i; |
---|
303 | |
---|
304 | cdata = gstreamer_find_mixer_channel (data); |
---|
305 | if (cdata == NULL) |
---|
306 | return; /* This is also sort of lame */ |
---|
307 | |
---|
308 | vol = gstreamer_trackify_volume (cdata->track, vol); |
---|
309 | |
---|
310 | volumes = g_new0 (gint, cdata->track->num_channels); |
---|
311 | for (i = 0; i < cdata->track->num_channels; ++i) { |
---|
312 | volumes[i] = vol; |
---|
313 | } |
---|
314 | gst_mixer_set_volume (cdata->mixer, cdata->track, volumes); |
---|
315 | g_free (volumes); |
---|
316 | } |
---|
317 | |
---|
318 | /* |
---|
319 | This code is stolen from gnome-media's gst-mixer |
---|
320 | */ |
---|
321 | static void |
---|
322 | gstreamer_discover_mixers (MixerData *data) |
---|
323 | { |
---|
324 | const GList *elements; |
---|
325 | gint num = 0, channel_count = 0; |
---|
326 | |
---|
327 | /* go through all elements of a certain class and check whether |
---|
328 | * they implement a mixer. If so, add a page */ |
---|
329 | elements = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY); |
---|
330 | for ( ; elements != NULL; elements = elements->next) { |
---|
331 | GstElementFactory *factory = GST_ELEMENT_FACTORY (elements->data); |
---|
332 | gchar *title = NULL; |
---|
333 | const gchar *klass; |
---|
334 | GstElement *element = NULL; |
---|
335 | const GParamSpec *devspec; |
---|
336 | GstPropertyProbe *probe; |
---|
337 | GValueArray *array = NULL; |
---|
338 | gint n; |
---|
339 | const GList *tracks; |
---|
340 | |
---|
341 | /* check category */ |
---|
342 | klass = gst_element_factory_get_klass (factory); |
---|
343 | if (strcmp (klass, "Generic/Audio")) |
---|
344 | goto next; |
---|
345 | |
---|
346 | /* create element */ |
---|
347 | title = g_strdup_printf ("gst-mixer-%d", num); |
---|
348 | element = gst_element_factory_create (factory, title); |
---|
349 | if (!element) |
---|
350 | goto next; |
---|
351 | |
---|
352 | if (!GST_IS_PROPERTY_PROBE (element)) |
---|
353 | goto next; |
---|
354 | |
---|
355 | probe = GST_PROPERTY_PROBE (element); |
---|
356 | if (!(devspec = gst_property_probe_get_property (probe, "device"))) |
---|
357 | goto next; |
---|
358 | if (!(array = gst_property_probe_probe_and_get_values (probe, devspec))) |
---|
359 | goto next; |
---|
360 | |
---|
361 | /* set all devices and test for mixer */ |
---|
362 | for (n = 0; n < array->n_values; n++) { |
---|
363 | GValue *device = g_value_array_get_nth (array, n); |
---|
364 | |
---|
365 | /* set this device */ |
---|
366 | g_object_set_property (G_OBJECT (element), "device", device); |
---|
367 | if (gst_element_set_state (element, |
---|
368 | GST_STATE_READY) == GST_STATE_FAILURE) |
---|
369 | continue; |
---|
370 | |
---|
371 | /* Is this device a mixer? If so, add it to the list. */ |
---|
372 | if (!GST_IS_MIXER (element)) { |
---|
373 | gst_element_set_state (element, GST_STATE_NULL); |
---|
374 | continue; |
---|
375 | } |
---|
376 | |
---|
377 | tracks = gst_mixer_list_tracks (GST_MIXER (element)); |
---|
378 | for (; tracks != NULL; tracks = tracks->next) { |
---|
379 | ChannelData *cdata; |
---|
380 | GstMixerTrack *track = tracks->data; |
---|
381 | |
---|
382 | cdata = g_new0 (ChannelData, 1); |
---|
383 | cdata->channel = channel_count; |
---|
384 | cdata->name = track->label; |
---|
385 | cdata->mixer = GST_MIXER (element); |
---|
386 | cdata->track = track; |
---|
387 | |
---|
388 | g_object_ref (cdata->mixer); |
---|
389 | g_object_ref (cdata->track); |
---|
390 | |
---|
391 | data->channels = g_list_append (data->channels, cdata); |
---|
392 | channel_count++; |
---|
393 | } |
---|
394 | |
---|
395 | num++; |
---|
396 | |
---|
397 | /* and recreate this object, since we give it to the mixer */ |
---|
398 | title = g_strdup_printf ("gst-mixer-%d", num); |
---|
399 | element = gst_element_factory_create (factory, title); |
---|
400 | } |
---|
401 | |
---|
402 | next: |
---|
403 | if (element) |
---|
404 | gst_object_unref (GST_OBJECT (element)); |
---|
405 | if (array) |
---|
406 | g_value_array_free (array); |
---|
407 | g_free (title); |
---|
408 | } |
---|
409 | } |
---|
410 | |
---|
411 | static void |
---|
412 | gstreamer_channel_data_free (ChannelData *cdata) |
---|
413 | { |
---|
414 | g_object_unref (cdata->mixer); |
---|
415 | g_object_unref (cdata->track); |
---|
416 | g_free (cdata); |
---|
417 | } |
---|
418 | |
---|
419 | static gboolean |
---|
420 | gstreamer_init (MixerData *data) |
---|
421 | { |
---|
422 | if (! gst_init_check (NULL, NULL)) |
---|
423 | return FALSE; |
---|
424 | gstreamer_discover_mixers (data); |
---|
425 | return TRUE; |
---|
426 | } |
---|
427 | |
---|
428 | #endif |
---|
429 | |
---|
430 | |
---|
431 | #ifdef IRIX_API |
---|
432 | /* |
---|
433 | * Note: we are using the obsolete API to increase portability. |
---|
434 | * /usr/sbin/audiopanel provides many more options... |
---|
435 | */ |
---|
436 | #define MAX_PV_BUF 4 |
---|
437 | long pv_buf[MAX_PV_BUF] = { |
---|
438 | AL_LEFT_SPEAKER_GAIN, 0L, AL_RIGHT_SPEAKER_GAIN, 0L |
---|
439 | }; |
---|
440 | #endif |
---|
441 | |
---|
442 | static gboolean |
---|
443 | key_writable (PanelApplet *applet, const char *key) |
---|
444 | { |
---|
445 | gboolean writable; |
---|
446 | char *fullkey; |
---|
447 | static GConfClient *client = NULL; |
---|
448 | if (client == NULL) |
---|
449 | client = gconf_client_get_default (); |
---|
450 | |
---|
451 | fullkey = panel_applet_gconf_get_full_key (applet, key); |
---|
452 | |
---|
453 | writable = gconf_client_key_is_writable (client, fullkey, NULL); |
---|
454 | |
---|
455 | g_free (fullkey); |
---|
456 | |
---|
457 | return writable; |
---|
458 | } |
---|
459 | |
---|
460 | #ifdef OSS_API |
---|
461 | static void |
---|
462 | get_channels (MixerData *data) |
---|
463 | { |
---|
464 | gint res, i; |
---|
465 | int devmask; |
---|
466 | gboolean valid = FALSE; |
---|
467 | |
---|
468 | res = ioctl(mixerfd, MIXER_READ(SOUND_MIXER_DEVMASK), &devmask); |
---|
469 | if (res != 0) { |
---|
470 | return; |
---|
471 | } |
---|
472 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { |
---|
473 | ChannelData *cdata; |
---|
474 | if (devmask & (1 << i)) { |
---|
475 | cdata = g_new0 (ChannelData, 1); |
---|
476 | cdata->channel = i; |
---|
477 | cdata->name = channel_names[i]; |
---|
478 | data->channels = g_list_append (data->channels, cdata); |
---|
479 | if (data->mixerchannel == i) |
---|
480 | valid = TRUE; |
---|
481 | } |
---|
482 | } |
---|
483 | /* selected channel does not work. Set it to something that does */ |
---|
484 | if (!valid) { |
---|
485 | ChannelData *cdata = data->channels->data; |
---|
486 | data->mixerchannel = cdata->channel; |
---|
487 | if (key_writable (PANEL_APPLET (data->applet), "channel")) |
---|
488 | panel_applet_gconf_set_int (PANEL_APPLET (data->applet), "channel", |
---|
489 | data->mixerchannel, NULL); |
---|
490 | } |
---|
491 | } |
---|
492 | #endif |
---|
493 | |
---|
494 | |
---|
495 | /********************** Mixer related Code *******************/ |
---|
496 | /* Mostly based on the gmix code */ |
---|
497 | /*************************************************************/ |
---|
498 | static gboolean |
---|
499 | openMixer(const gchar *device_name, MixerData *data) |
---|
500 | { |
---|
501 | gint res, ver; |
---|
502 | #ifdef OSS_API |
---|
503 | |
---|
504 | |
---|
505 | mixerfd = open(device_name, O_RDWR, 0); |
---|
506 | #endif |
---|
507 | #ifdef SUN_API |
---|
508 | mixerfd = open(device_name, O_RDWR); |
---|
509 | #endif |
---|
510 | #ifdef IRIX_API |
---|
511 | /* |
---|
512 | * This is a result code, not a file descriptor, and we ignore |
---|
513 | * the values read. But the call is useful to see if we can |
---|
514 | * access the default output port. |
---|
515 | */ |
---|
516 | mixerfd = ALgetparams(AL_DEFAULT_DEVICE, pv_buf, MAX_PV_BUF); |
---|
517 | #endif |
---|
518 | #ifdef AIX_API |
---|
519 | gchar iomixchannel[MAX_MPX_CHAN_LEN]; |
---|
520 | gchar mstvolchannel[MAX_MPX_CHAN_LEN]; |
---|
521 | |
---|
522 | /* Construct the string to use the iomix multiplex channel |
---|
523 | * of the device */ |
---|
524 | sprintf(iomixchannel, "%s%s", device_name, "/iomix"); |
---|
525 | sprintf(mstvolchannel, "%s%s", device_name, "/mstvol"); |
---|
526 | |
---|
527 | mstvolfd = open (mstvolchannel, O_WRONLY); |
---|
528 | if (mstvolfd < 0) { |
---|
529 | /* probably should die more gracefully */ |
---|
530 | return FALSE; |
---|
531 | } |
---|
532 | |
---|
533 | mixerfd = open(iomixchannel, O_WRONLY); |
---|
534 | #endif |
---|
535 | if (mixerfd < 0) { |
---|
536 | /* probably should die more gracefully */ |
---|
537 | return FALSE; |
---|
538 | } |
---|
539 | |
---|
540 | /* |
---|
541 | * check driver-version |
---|
542 | */ |
---|
543 | #ifdef OSS_GETVERSION |
---|
544 | res=ioctl(mixerfd, OSS_GETVERSION, &ver); |
---|
545 | if ((res==0) && (ver!=SOUND_VERSION)) { |
---|
546 | g_message(_("warning: this version of gmix was compiled " |
---|
547 | "with a different version of\nsoundcard.h.\n")); |
---|
548 | } |
---|
549 | #endif |
---|
550 | #ifdef OSS_API |
---|
551 | /* |
---|
552 | * Check whether this mixer actually supports the channel |
---|
553 | * we're going to try to monitor. |
---|
554 | */ |
---|
555 | get_channels (data); |
---|
556 | |
---|
557 | #endif |
---|
558 | return TRUE; |
---|
559 | } |
---|
560 | |
---|
561 | /* only works with master vol currently */ |
---|
562 | static int |
---|
563 | readMixer(MixerData *data) |
---|
564 | { |
---|
565 | #ifdef GSTREAMER_API |
---|
566 | return gstreamer_get_volume (data); |
---|
567 | #endif |
---|
568 | #ifdef OSS_API |
---|
569 | gint vol, r, l; |
---|
570 | /* if we couldn't open the mixer */ |
---|
571 | if (mixerfd < 0) return 0; |
---|
572 | |
---|
573 | ioctl(mixerfd, MIXER_READ(data->mixerchannel), &vol); |
---|
574 | |
---|
575 | l = vol & 0xff; |
---|
576 | r = (vol & 0xff00) >> 8; |
---|
577 | /* printf("vol=%d l=%d r=%d\n",vol, l, r); */ |
---|
578 | |
---|
579 | return (r+l)/2; |
---|
580 | #endif |
---|
581 | #ifdef SUN_API |
---|
582 | audio_info_t ainfo; |
---|
583 | AUDIO_INITINFO (&ainfo); |
---|
584 | ioctl (mixerfd, AUDIO_GETINFO, &ainfo); |
---|
585 | return (ainfo.play.gain); |
---|
586 | #endif |
---|
587 | #ifdef IRIX_API |
---|
588 | /* |
---|
589 | * Average the current gain settings. If we can't read the |
---|
590 | * current levels use the values from the previous read. |
---|
591 | */ |
---|
592 | (void) ALgetparams(AL_DEFAULT_DEVICE, pv_buf, MAX_PV_BUF); |
---|
593 | return (pv_buf[1] + pv_buf[3]) / 2; |
---|
594 | #endif |
---|
595 | #ifdef AIX_API |
---|
596 | gint rc; |
---|
597 | audio_channel_status info_channels; |
---|
598 | |
---|
599 | info_channels.channel_count = 0; |
---|
600 | rc = ioctl (mixerfd, AUDIO_CHANNEL_STATUS, &info_channels); |
---|
601 | if (rc < 0) return DEFAULT_MST_VOLUME; |
---|
602 | else return info_channels.master_volume; |
---|
603 | #endif /* AIX_API */ |
---|
604 | } |
---|
605 | |
---|
606 | static void |
---|
607 | setMixer(gint vol, MixerData *data) |
---|
608 | { |
---|
609 | gint tvol; |
---|
610 | #ifdef GSTREAMER_API |
---|
611 | gstreamer_set_volume (data, vol); |
---|
612 | #endif |
---|
613 | #ifdef OSS_API |
---|
614 | /* if we couldn't open the mixer */ |
---|
615 | if (mixerfd < 0) return; |
---|
616 | |
---|
617 | tvol = (vol << 8) + vol; |
---|
618 | /*g_message("Saving mixer value of %d",tvol);*/ |
---|
619 | ioctl(mixerfd, MIXER_WRITE(data->mixerchannel), &tvol); |
---|
620 | |
---|
621 | /* SOUND_MIXER_SPEAKER is output level on Mac, but input level on PC. #96639 */ |
---|
622 | #ifdef __powerpc__ |
---|
623 | ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_SPEAKER), &tvol); |
---|
624 | #endif |
---|
625 | #endif |
---|
626 | #ifdef SUN_API |
---|
627 | audio_info_t ainfo; |
---|
628 | AUDIO_INITINFO (&ainfo); |
---|
629 | ainfo.play.gain = vol; |
---|
630 | ioctl (mixerfd, AUDIO_SETINFO, &ainfo); |
---|
631 | #endif |
---|
632 | #ifdef IRIX_API |
---|
633 | if (vol < 0) |
---|
634 | tvol = 0; |
---|
635 | else if (vol > VOLUME_MAX) |
---|
636 | tvol = VOLUME_MAX; |
---|
637 | else |
---|
638 | tvol = vol; |
---|
639 | |
---|
640 | pv_buf[1] = pv_buf[3] = tvol; |
---|
641 | (void) ALsetparams(AL_DEFAULT_DEVICE, pv_buf, MAX_PV_BUF); |
---|
642 | #endif |
---|
643 | #ifdef AIX_API |
---|
644 | int master_volume; |
---|
645 | |
---|
646 | master_volume = (int)CLAMP (vol, 0, VOLUME_MAX); |
---|
647 | ioctl (mstvolfd, AUDIO_MASTER_VOLUME, &master_volume); |
---|
648 | #endif |
---|
649 | } |
---|
650 | |
---|
651 | static void |
---|
652 | mixer_value_changed_cb (GtkAdjustment *adj, MixerData *data) |
---|
653 | { |
---|
654 | gint vol; |
---|
655 | |
---|
656 | vol = adj->value; |
---|
657 | |
---|
658 | data->vol = vol; |
---|
659 | |
---|
660 | mixer_update_image (data); |
---|
661 | |
---|
662 | if (!data->mute) { |
---|
663 | setMixer (vol, data); |
---|
664 | } |
---|
665 | } |
---|
666 | |
---|
667 | #ifdef SUN_API |
---|
668 | static gboolean |
---|
669 | set_mute_status (gboolean mute_state) |
---|
670 | { |
---|
671 | audio_info_t ainfo; |
---|
672 | AUDIO_INITINFO (&ainfo); |
---|
673 | ainfo.output_muted = mute_state; |
---|
674 | ioctl (mixerfd, AUDIO_SETINFO, &ainfo); |
---|
675 | } |
---|
676 | |
---|
677 | static gboolean |
---|
678 | get_mute_status () |
---|
679 | { |
---|
680 | audio_info_t ainfo; |
---|
681 | AUDIO_INITINFO (&ainfo); |
---|
682 | ioctl (mixerfd, AUDIO_GETINFO, &ainfo); |
---|
683 | return (ainfo.output_muted); |
---|
684 | } |
---|
685 | #endif |
---|
686 | |
---|
687 | static gboolean |
---|
688 | mixer_timeout_cb (MixerData *data) |
---|
689 | { |
---|
690 | BonoboUIComponent *component; |
---|
691 | gint vol; |
---|
692 | gboolean state; |
---|
693 | |
---|
694 | if (!device_present) { |
---|
695 | data->mute = TRUE; |
---|
696 | mixer_update_image (data); |
---|
697 | |
---|
698 | gtk_tooltips_set_tip (data->tooltips, |
---|
699 | data->applet, |
---|
700 | _(access_name_nodevice), |
---|
701 | NULL); |
---|
702 | if (gail_loaded) { |
---|
703 | add_atk_namedesc (data->applet, |
---|
704 | _(access_name_nodevice), |
---|
705 | NULL); |
---|
706 | } |
---|
707 | return TRUE; |
---|
708 | } |
---|
709 | |
---|
710 | #ifdef SUN_API |
---|
711 | state = get_mute_status (); |
---|
712 | |
---|
713 | if (state != data->mute) { |
---|
714 | data->mute = state; |
---|
715 | if (data->mute) { |
---|
716 | mixer_update_image (data); |
---|
717 | |
---|
718 | |
---|
719 | gtk_tooltips_set_tip (data->tooltips, |
---|
720 | data->applet, |
---|
721 | _(access_name_mute), |
---|
722 | NULL); |
---|
723 | if (gail_loaded) { |
---|
724 | add_atk_namedesc (data->applet, |
---|
725 | _(access_name_mute), |
---|
726 | NULL); |
---|
727 | } |
---|
728 | component = panel_applet_get_popup_component (PANEL_APPLET (data->applet)); |
---|
729 | bonobo_ui_component_set_prop (component, |
---|
730 | "/commands/Mute", |
---|
731 | "state", |
---|
732 | "1", |
---|
733 | NULL); |
---|
734 | |
---|
735 | } else { |
---|
736 | mixer_update_image (data); |
---|
737 | |
---|
738 | gtk_tooltips_set_tip (data->tooltips, |
---|
739 | data->applet, |
---|
740 | _(access_name), |
---|
741 | NULL); |
---|
742 | if (gail_loaded) { |
---|
743 | add_atk_namedesc (data->applet, |
---|
744 | _(access_name), |
---|
745 | NULL); |
---|
746 | } |
---|
747 | component = panel_applet_get_popup_component (PANEL_APPLET (data->applet)); |
---|
748 | bonobo_ui_component_set_prop (component, |
---|
749 | "/commands/Mute", |
---|
750 | "state", |
---|
751 | "0", |
---|
752 | NULL); |
---|
753 | |
---|
754 | } |
---|
755 | } |
---|
756 | #endif |
---|
757 | vol = readMixer (data); |
---|
758 | |
---|
759 | #ifndef SUN_API |
---|
760 | /* Some external program changed the volume, get out of mute mode. */ |
---|
761 | if (data->mute && (vol > 0)) { |
---|
762 | data->mute = FALSE; |
---|
763 | |
---|
764 | /* Sync the menu. */ |
---|
765 | component = panel_applet_get_popup_component (PANEL_APPLET (data->applet)); |
---|
766 | |
---|
767 | bonobo_ui_component_set_prop (component, |
---|
768 | "/commands/Mute", |
---|
769 | "state", |
---|
770 | "0", |
---|
771 | NULL); |
---|
772 | |
---|
773 | gtk_tooltips_set_tip (data->tooltips, |
---|
774 | data->applet, |
---|
775 | _(access_name), |
---|
776 | NULL); |
---|
777 | if (gail_loaded) { |
---|
778 | add_atk_namedesc (data->applet, _(access_name), NULL); |
---|
779 | } |
---|
780 | } |
---|
781 | #endif |
---|
782 | |
---|
783 | if (!data->mute && vol != data->vol) { |
---|
784 | data->vol = vol; |
---|
785 | mixer_update_slider (data); |
---|
786 | } |
---|
787 | |
---|
788 | |
---|
789 | return TRUE; |
---|
790 | } |
---|
791 | |
---|
792 | static void |
---|
793 | mixer_update_slider (MixerData *data) |
---|
794 | { |
---|
795 | gint vol = data->vol; |
---|
796 | |
---|
797 | gtk_adjustment_set_value (data->adj, vol); |
---|
798 | } |
---|
799 | |
---|
800 | static void |
---|
801 | mixer_load_volume_images (MixerData *data) { |
---|
802 | if (data->zero) |
---|
803 | g_object_unref (data->zero); |
---|
804 | if (data->min) |
---|
805 | g_object_unref (data->min); |
---|
806 | if (data->med) |
---|
807 | g_object_unref (data->med); |
---|
808 | if (data->max) |
---|
809 | g_object_unref (data->max); |
---|
810 | if (data->muted) |
---|
811 | g_object_unref (data->muted); |
---|
812 | |
---|
813 | data->zero = gtk_widget_render_icon (data->applet, VOLUME_STOCK_ZERO, |
---|
814 | volume_icon_size, NULL); |
---|
815 | data->min = gtk_widget_render_icon (data->applet, VOLUME_STOCK_MIN, |
---|
816 | volume_icon_size, NULL); |
---|
817 | data->med = gtk_widget_render_icon (data->applet, VOLUME_STOCK_MED, |
---|
818 | volume_icon_size, NULL); |
---|
819 | data->max = gtk_widget_render_icon (data->applet, VOLUME_STOCK_MAX, |
---|
820 | volume_icon_size, NULL); |
---|
821 | data->muted = gtk_widget_render_icon (data->applet, VOLUME_STOCK_MUTE, |
---|
822 | volume_icon_size, NULL); |
---|
823 | } |
---|
824 | |
---|
825 | static void |
---|
826 | applet_size_allocate (GtkWidget *widget, GtkAllocation *allocation, MixerData *data) |
---|
827 | { |
---|
828 | if (IS_PANEL_HORIZONTAL (data->orientation)) { |
---|
829 | if (data->panel_size != allocation->height) { |
---|
830 | data->panel_size = allocation->height; |
---|
831 | mixer_update_image (data); |
---|
832 | } |
---|
833 | } |
---|
834 | else { |
---|
835 | if (data->panel_size != allocation->width) { |
---|
836 | data->panel_size = allocation->width; |
---|
837 | mixer_update_image (data); |
---|
838 | } |
---|
839 | } |
---|
840 | |
---|
841 | } |
---|
842 | |
---|
843 | |
---|
844 | static void |
---|
845 | mixer_update_image (MixerData *data) |
---|
846 | { |
---|
847 | gint vol, size; |
---|
848 | GdkPixbuf *pixbuf, *copy, *scaled = NULL; |
---|
849 | vol = data->vol; |
---|
850 | |
---|
851 | if (vol <= 0) |
---|
852 | pixbuf = gdk_pixbuf_copy (data->zero); |
---|
853 | else if (vol <= VOLUME_MAX / 3) |
---|
854 | pixbuf = gdk_pixbuf_copy (data->min); |
---|
855 | else if (vol <= 2 * VOLUME_MAX / 3) |
---|
856 | pixbuf = gdk_pixbuf_copy (data->med); |
---|
857 | else |
---|
858 | pixbuf = gdk_pixbuf_copy (data->max); |
---|
859 | |
---|
860 | if (data->mute) { |
---|
861 | GdkPixbuf *mute = gdk_pixbuf_copy (data->muted); |
---|
862 | |
---|
863 | gdk_pixbuf_composite (mute, |
---|
864 | pixbuf, |
---|
865 | 0, |
---|
866 | 0, |
---|
867 | gdk_pixbuf_get_width (mute), |
---|
868 | gdk_pixbuf_get_height (mute), |
---|
869 | 0, |
---|
870 | 0, |
---|
871 | 1.0, |
---|
872 | 1.0, |
---|
873 | GDK_INTERP_BILINEAR, |
---|
874 | 255); |
---|
875 | g_object_unref (mute); |
---|
876 | } |
---|
877 | |
---|
878 | size = MAX (11, data->panel_size); |
---|
879 | scaled = gdk_pixbuf_scale_simple (pixbuf, size, size, GDK_INTERP_BILINEAR); |
---|
880 | gtk_image_set_from_pixbuf (GTK_IMAGE (data->image), scaled); |
---|
881 | g_object_unref (scaled); |
---|
882 | g_object_unref (pixbuf); |
---|
883 | |
---|
884 | if (key_writable (PANEL_APPLET (data->applet), "vol")) |
---|
885 | panel_applet_gconf_set_int (PANEL_APPLET (data->applet), "vol", data->vol, |
---|
886 | NULL); |
---|
887 | if (key_writable (PANEL_APPLET (data->applet), "mute")) |
---|
888 | panel_applet_gconf_set_bool (PANEL_APPLET (data->applet), "mute", data->mute, |
---|
889 | NULL); |
---|
890 | |
---|
891 | } |
---|
892 | |
---|
893 | static gboolean |
---|
894 | scale_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, MixerData *data) |
---|
895 | { |
---|
896 | switch (event->keyval) { |
---|
897 | case GDK_Escape: |
---|
898 | /* Revert. */ |
---|
899 | mixer_popup_hide (data, TRUE); |
---|
900 | return TRUE; |
---|
901 | |
---|
902 | case GDK_KP_Enter: |
---|
903 | case GDK_ISO_Enter: |
---|
904 | case GDK_3270_Enter: |
---|
905 | case GDK_Return: |
---|
906 | case GDK_space: |
---|
907 | case GDK_KP_Space: |
---|
908 | /* Apply. */ |
---|
909 | mixer_popup_hide (data, FALSE); |
---|
910 | return TRUE; |
---|
911 | |
---|
912 | default: |
---|
913 | break; |
---|
914 | } |
---|
915 | |
---|
916 | return FALSE; |
---|
917 | } |
---|
918 | |
---|
919 | static void |
---|
920 | error_response (GtkDialog *dialog, gint id, MixerData *data) |
---|
921 | { |
---|
922 | gtk_widget_destroy (data->error_dialog); |
---|
923 | data->error_dialog = NULL; |
---|
924 | } |
---|
925 | |
---|
926 | static void |
---|
927 | show_error_dialog (MixerData *data) |
---|
928 | { |
---|
929 | if (data->error_dialog) { |
---|
930 | gtk_window_present (GTK_WINDOW (data->error_dialog)); |
---|
931 | return; |
---|
932 | } |
---|
933 | data->error_dialog = gtk_message_dialog_new (NULL, |
---|
934 | GTK_DIALOG_DESTROY_WITH_PARENT, |
---|
935 | GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, |
---|
936 | _("Couldn't open mixer device %s\n"), |
---|
937 | data->device, NULL); |
---|
938 | |
---|
939 | gtk_window_set_screen (GTK_WINDOW (data->error_dialog), |
---|
940 | gtk_widget_get_screen (GTK_WIDGET (data->applet))); |
---|
941 | g_signal_connect (GTK_OBJECT (data->error_dialog), |
---|
942 | "response", |
---|
943 | G_CALLBACK (error_response), |
---|
944 | data); |
---|
945 | gtk_widget_show_all (data->error_dialog); |
---|
946 | } |
---|
947 | |
---|
948 | static gboolean |
---|
949 | scale_button_release_event_cb (GtkWidget *widget, GdkEventButton *event, MixerData *data) |
---|
950 | { |
---|
951 | |
---|
952 | if (data->popup != NULL) { |
---|
953 | mixer_popup_hide (data, FALSE); |
---|
954 | return TRUE; |
---|
955 | } |
---|
956 | |
---|
957 | return FALSE; |
---|
958 | } |
---|
959 | |
---|
960 | static gboolean |
---|
961 | scale_button_event (GtkWidget *widget, GdkEventButton *event, MixerData *data) |
---|
962 | { |
---|
963 | return TRUE; |
---|
964 | } |
---|
965 | |
---|
966 | static gboolean |
---|
967 | applet_button_release_event_cb (GtkWidget *widget, GdkEventButton *event, MixerData *data) |
---|
968 | { |
---|
969 | |
---|
970 | if (event->button == 1) { |
---|
971 | if (data->popup == NULL && device_present) { |
---|
972 | mixer_popup_show (data); |
---|
973 | return TRUE; |
---|
974 | } else if (!device_present) |
---|
975 | show_error_dialog (data); |
---|
976 | } |
---|
977 | |
---|
978 | return FALSE; |
---|
979 | } |
---|
980 | |
---|
981 | static gboolean |
---|
982 | applet_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, MixerData *data) |
---|
983 | { |
---|
984 | switch (event->keyval) { |
---|
985 | case GDK_Escape: |
---|
986 | /* Revert. */ |
---|
987 | mixer_popup_hide (data, TRUE); |
---|
988 | return TRUE; |
---|
989 | case GDK_m: |
---|
990 | if (event->state == GDK_CONTROL_MASK) { |
---|
991 | Bonobo_UIComponent_EventType type; |
---|
992 | if (data->mute) |
---|
993 | mixer_ui_component_event (NULL, "Mute", type, "0", data); |
---|
994 | else |
---|
995 | mixer_ui_component_event (NULL, "Mute", type, "1", data); |
---|
996 | return TRUE; |
---|
997 | } |
---|
998 | break; |
---|
999 | case GDK_o: |
---|
1000 | if (event->state == GDK_CONTROL_MASK) { |
---|
1001 | mixer_start_gmix_cb (NULL, data, NULL); |
---|
1002 | return TRUE; |
---|
1003 | } |
---|
1004 | break; |
---|
1005 | case GDK_KP_Enter: |
---|
1006 | case GDK_ISO_Enter: |
---|
1007 | case GDK_3270_Enter: |
---|
1008 | case GDK_Return: |
---|
1009 | case GDK_space: |
---|
1010 | case GDK_KP_Space: |
---|
1011 | /* Apply. */ |
---|
1012 | if (data->popup != NULL) |
---|
1013 | mixer_popup_hide (data, FALSE); |
---|
1014 | else if(device_present) |
---|
1015 | mixer_popup_show (data); |
---|
1016 | else |
---|
1017 | show_error_dialog (data); |
---|
1018 | return TRUE; |
---|
1019 | |
---|
1020 | default: |
---|
1021 | break; |
---|
1022 | } |
---|
1023 | |
---|
1024 | return FALSE; |
---|
1025 | } |
---|
1026 | |
---|
1027 | |
---|
1028 | static gboolean |
---|
1029 | applet_scroll_event_cb (GtkWidget *widget, GdkEventScroll *event, MixerData *data) |
---|
1030 | { |
---|
1031 | gint direction; |
---|
1032 | if (event->type != GDK_SCROLL ) { |
---|
1033 | return FALSE; |
---|
1034 | } |
---|
1035 | |
---|
1036 | direction = event->direction; |
---|
1037 | |
---|
1038 | switch(direction) { |
---|
1039 | case GDK_SCROLL_UP: |
---|
1040 | data->vol += 5; |
---|
1041 | break; |
---|
1042 | case GDK_SCROLL_DOWN: |
---|
1043 | data->vol -= 5; |
---|
1044 | break; |
---|
1045 | default: |
---|
1046 | break; |
---|
1047 | } |
---|
1048 | |
---|
1049 | if (data->vol > VOLUME_MAX) data->vol = VOLUME_MAX; |
---|
1050 | if (data->vol < 0) data->vol = 0; |
---|
1051 | |
---|
1052 | mixer_update_slider(data); |
---|
1053 | |
---|
1054 | return TRUE; |
---|
1055 | } |
---|
1056 | |
---|
1057 | static void |
---|
1058 | mixer_popup_show (MixerData *data) |
---|
1059 | { |
---|
1060 | GtkRequisition req; |
---|
1061 | GtkWidget *frame; |
---|
1062 | GtkWidget *inner_frame; |
---|
1063 | GtkWidget *pluslabel, *minuslabel; |
---|
1064 | GtkWidget *event; |
---|
1065 | GtkWidget *box; |
---|
1066 | gint x, y; |
---|
1067 | gint width, height; |
---|
1068 | GdkGrabStatus pointer, keyboard; |
---|
1069 | |
---|
1070 | data->popup = gtk_window_new (GTK_WINDOW_POPUP); |
---|
1071 | gtk_window_set_screen (GTK_WINDOW (data->popup), |
---|
1072 | gtk_widget_get_screen (data->applet)); |
---|
1073 | |
---|
1074 | data->vol_before_popup = readMixer (data); |
---|
1075 | |
---|
1076 | frame = gtk_frame_new (NULL); |
---|
1077 | gtk_container_set_border_width (GTK_CONTAINER (frame), 0); |
---|
1078 | gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); |
---|
1079 | gtk_widget_show (frame); |
---|
1080 | |
---|
1081 | inner_frame = gtk_frame_new (NULL); |
---|
1082 | gtk_container_set_border_width (GTK_CONTAINER (inner_frame), 0); |
---|
1083 | gtk_frame_set_shadow_type (GTK_FRAME (inner_frame), GTK_SHADOW_NONE); |
---|
1084 | gtk_widget_show (inner_frame); |
---|
1085 | |
---|
1086 | event = gtk_event_box_new (); |
---|
1087 | /* This signal is to not let button press close the popup when the press is |
---|
1088 | ** in the scale */ |
---|
1089 | g_signal_connect_after (event, "button_press_event", |
---|
1090 | G_CALLBACK (scale_button_event), data); |
---|
1091 | |
---|
1092 | if (IS_PANEL_HORIZONTAL (data->orientation)) { |
---|
1093 | box = gtk_vbox_new (FALSE, 0); |
---|
1094 | data->scale = gtk_vscale_new (data->adj); |
---|
1095 | gtk_widget_set_size_request (data->scale, -1, 100); |
---|
1096 | gtk_range_set_inverted (GTK_RANGE (data->scale), TRUE); |
---|
1097 | } else { |
---|
1098 | box = gtk_hbox_new (FALSE, 0); |
---|
1099 | data->scale = gtk_hscale_new (data->adj); |
---|
1100 | gtk_widget_set_size_request (data->scale, 100, -1); |
---|
1101 | } |
---|
1102 | |
---|
1103 | if (gail_loaded) { |
---|
1104 | add_atk_namedesc (data->scale, |
---|
1105 | _("Volume Controller"), |
---|
1106 | _("Use Up/Down arrow keys to change volume")); |
---|
1107 | } |
---|
1108 | g_signal_connect_after (data->popup, |
---|
1109 | "button-press-event", |
---|
1110 | (GCallback) scale_button_release_event_cb, |
---|
1111 | data); |
---|
1112 | |
---|
1113 | g_signal_connect (data->scale, |
---|
1114 | "key-press-event", |
---|
1115 | (GCallback) scale_key_press_event_cb, |
---|
1116 | data); |
---|
1117 | |
---|
1118 | gtk_widget_show (data->scale); |
---|
1119 | gtk_scale_set_draw_value (GTK_SCALE (data->scale), FALSE); |
---|
1120 | |
---|
1121 | gtk_range_set_update_policy (GTK_RANGE (data->scale), |
---|
1122 | GTK_UPDATE_CONTINUOUS); |
---|
1123 | |
---|
1124 | gtk_container_add (GTK_CONTAINER (data->popup), frame); |
---|
1125 | |
---|
1126 | gtk_container_add (GTK_CONTAINER (frame), inner_frame); |
---|
1127 | |
---|
1128 | /* Translators - The + and - refer to increasing and decreasing the volume. |
---|
1129 | ** I don't know if there are sensible alternatives in other languages */ |
---|
1130 | pluslabel = gtk_label_new (_("+")); |
---|
1131 | minuslabel = gtk_label_new (_("-")); |
---|
1132 | |
---|
1133 | if (IS_PANEL_HORIZONTAL (data->orientation)) { |
---|
1134 | gtk_box_pack_start (GTK_BOX (box), pluslabel, FALSE, FALSE, 0); |
---|
1135 | gtk_box_pack_end (GTK_BOX (box), minuslabel, FALSE, FALSE, 0); |
---|
1136 | } else { |
---|
1137 | gtk_box_pack_start (GTK_BOX (box), minuslabel, FALSE, FALSE, 0); |
---|
1138 | gtk_box_pack_end (GTK_BOX (box), pluslabel, FALSE, FALSE, 0); |
---|
1139 | } |
---|
1140 | gtk_box_pack_start (GTK_BOX (box), data->scale, TRUE, TRUE, 0); |
---|
1141 | |
---|
1142 | gtk_container_add (GTK_CONTAINER (event), box); |
---|
1143 | gtk_container_add (GTK_CONTAINER (inner_frame), event); |
---|
1144 | |
---|
1145 | gtk_widget_show_all (inner_frame); |
---|
1146 | |
---|
1147 | /* |
---|
1148 | * Position the popup right next to the button. |
---|
1149 | */ |
---|
1150 | gtk_widget_size_request (data->popup, &req); |
---|
1151 | |
---|
1152 | gdk_window_get_origin (data->image->window, &x, &y); |
---|
1153 | gdk_drawable_get_size (data->image->window, &width, &height); |
---|
1154 | |
---|
1155 | switch (data->orientation) { |
---|
1156 | case PANEL_APPLET_ORIENT_DOWN: |
---|
1157 | x += (width - req.width) / 2; |
---|
1158 | y += height; |
---|
1159 | break; |
---|
1160 | |
---|
1161 | case PANEL_APPLET_ORIENT_UP: |
---|
1162 | x += (width - req.width) / 2; |
---|
1163 | y -= req.height; |
---|
1164 | break; |
---|
1165 | |
---|
1166 | case PANEL_APPLET_ORIENT_LEFT: |
---|
1167 | x -= req.width; |
---|
1168 | y += (height - req.height) / 2; |
---|
1169 | break; |
---|
1170 | |
---|
1171 | case PANEL_APPLET_ORIENT_RIGHT: |
---|
1172 | x += width; |
---|
1173 | y += (height - req.height) / 2; |
---|
1174 | break; |
---|
1175 | |
---|
1176 | default: |
---|
1177 | break; |
---|
1178 | } |
---|
1179 | |
---|
1180 | if (x < 0) { |
---|
1181 | x = 0; |
---|
1182 | } |
---|
1183 | if (y < 0) { |
---|
1184 | y = 0; |
---|
1185 | } |
---|
1186 | |
---|
1187 | gtk_window_move (GTK_WINDOW (data->popup), x, y); |
---|
1188 | |
---|
1189 | gtk_widget_show (data->popup); |
---|
1190 | |
---|
1191 | /* |
---|
1192 | * Grab focus and pointer. |
---|
1193 | */ |
---|
1194 | gtk_widget_grab_focus (data->popup); |
---|
1195 | gtk_grab_add (data->popup); |
---|
1196 | |
---|
1197 | pointer = gdk_pointer_grab (data->popup->window, |
---|
1198 | TRUE, |
---|
1199 | (GDK_BUTTON_PRESS_MASK | |
---|
1200 | GDK_BUTTON_RELEASE_MASK |
---|
1201 | | GDK_POINTER_MOTION_MASK), |
---|
1202 | NULL, NULL, GDK_CURRENT_TIME); |
---|
1203 | |
---|
1204 | keyboard = gdk_keyboard_grab (data->popup->window, |
---|
1205 | TRUE, |
---|
1206 | GDK_CURRENT_TIME); |
---|
1207 | |
---|
1208 | if (pointer != GDK_GRAB_SUCCESS || keyboard != GDK_GRAB_SUCCESS) { |
---|
1209 | /* We could not grab. */ |
---|
1210 | gtk_grab_remove (data->popup); |
---|
1211 | gtk_widget_destroy (data->popup); |
---|
1212 | data->popup = NULL; |
---|
1213 | data->scale = NULL; |
---|
1214 | |
---|
1215 | if (pointer == GDK_GRAB_SUCCESS) { |
---|
1216 | gdk_keyboard_ungrab (GDK_CURRENT_TIME); |
---|
1217 | } |
---|
1218 | if (keyboard == GDK_GRAB_SUCCESS) { |
---|
1219 | gdk_pointer_ungrab (GDK_CURRENT_TIME); |
---|
1220 | } |
---|
1221 | |
---|
1222 | g_warning ("mixer_applet2: Could not grab X server."); |
---|
1223 | return; |
---|
1224 | } |
---|
1225 | |
---|
1226 | gtk_frame_set_shadow_type (GTK_FRAME (data->frame), GTK_SHADOW_IN); |
---|
1227 | } |
---|
1228 | |
---|
1229 | static void |
---|
1230 | mixer_popup_hide (MixerData *data, gboolean revert) |
---|
1231 | { |
---|
1232 | gint vol; |
---|
1233 | |
---|
1234 | if (data->popup) { |
---|
1235 | gtk_grab_remove (data->scale); |
---|
1236 | gdk_pointer_ungrab (GDK_CURRENT_TIME); |
---|
1237 | gdk_keyboard_ungrab (GDK_CURRENT_TIME); |
---|
1238 | |
---|
1239 | /* Ref the adjustment or it will get destroyed |
---|
1240 | * when the scale goes away. |
---|
1241 | */ |
---|
1242 | g_object_ref (G_OBJECT (data->adj)); |
---|
1243 | |
---|
1244 | gtk_widget_destroy (GTK_WIDGET (data->popup)); |
---|
1245 | |
---|
1246 | data->popup = NULL; |
---|
1247 | data->scale = NULL; |
---|
1248 | |
---|
1249 | if (revert) { |
---|
1250 | vol = data->vol_before_popup; |
---|
1251 | setMixer (vol, data); |
---|
1252 | } else { |
---|
1253 | /* Get a soft beep like sound to play here, like |
---|
1254 | * on MacOS X :) */ |
---|
1255 | /*gnome_sound_play ("blipp.wav");*/ |
---|
1256 | } |
---|
1257 | |
---|
1258 | gtk_frame_set_shadow_type (GTK_FRAME (data->frame), GTK_SHADOW_NONE); |
---|
1259 | } |
---|
1260 | } |
---|
1261 | |
---|
1262 | static void |
---|
1263 | destroy_mixer_cb (GtkWidget *widget, MixerData *data) |
---|
1264 | { |
---|
1265 | |
---|
1266 | #ifdef GSTREAMER_API |
---|
1267 | GList *iter; |
---|
1268 | for (iter = data->channels; iter != NULL; iter = iter->next) { |
---|
1269 | ChannelData *cdata = iter->data; |
---|
1270 | gstreamer_channel_data_free (cdata); |
---|
1271 | } |
---|
1272 | #endif |
---|
1273 | |
---|
1274 | #ifdef OSS_API |
---|
1275 | GList *iter; |
---|
1276 | for (iter = data->channels; iter != NULL; iter = iter->next) { |
---|
1277 | ChannelData *cdata = iter->data; |
---|
1278 | g_free (cdata); |
---|
1279 | } |
---|
1280 | #endif |
---|
1281 | |
---|
1282 | if (data->channels != NULL) |
---|
1283 | g_list_free (data->channels); |
---|
1284 | |
---|
1285 | if (data->timeout != 0) { |
---|
1286 | g_source_remove (data->timeout); |
---|
1287 | data->timeout = 0; |
---|
1288 | } |
---|
1289 | if (data->about_dialog) |
---|
1290 | gtk_widget_destroy (data->about_dialog); |
---|
1291 | if (data->error_dialog) |
---|
1292 | gtk_widget_destroy (data->error_dialog); |
---|
1293 | if (data->tooltips) |
---|
1294 | g_object_unref (data->tooltips); |
---|
1295 | if (data->device) |
---|
1296 | g_free (data->device); |
---|
1297 | g_free (data); |
---|
1298 | } |
---|
1299 | |
---|
1300 | static void |
---|
1301 | applet_change_background_cb (PanelApplet *applet, |
---|
1302 | PanelAppletBackgroundType type, |
---|
1303 | GdkColor *color, |
---|
1304 | GdkPixmap *pixmap, |
---|
1305 | MixerData *data) |
---|
1306 | { |
---|
1307 | if (type == PANEL_NO_BACKGROUND) { |
---|
1308 | GtkRcStyle *rc_style = gtk_rc_style_new (); |
---|
1309 | |
---|
1310 | gtk_widget_modify_style (data->applet, rc_style); |
---|
1311 | gtk_rc_style_unref (rc_style); |
---|
1312 | } |
---|
1313 | else if (type == PANEL_COLOR_BACKGROUND) { |
---|
1314 | gtk_widget_modify_bg (data->applet, |
---|
1315 | GTK_STATE_NORMAL, |
---|
1316 | color); |
---|
1317 | } else { /* pixmap */ |
---|
1318 | /* FIXME: Handle this when the panel support works again */ |
---|
1319 | } |
---|
1320 | } |
---|
1321 | |
---|
1322 | static void |
---|
1323 | applet_change_orient_cb (GtkWidget *w, PanelAppletOrient o, MixerData *data) |
---|
1324 | { |
---|
1325 | mixer_popup_hide (data, FALSE); |
---|
1326 | |
---|
1327 | data->orientation = o; |
---|
1328 | } |
---|
1329 | |
---|
1330 | static void |
---|
1331 | applet_change_size_cb (GtkWidget *w, gint size, MixerData *data) |
---|
1332 | { |
---|
1333 | mixer_popup_hide (data, FALSE); |
---|
1334 | mixer_update_image (data); |
---|
1335 | } |
---|
1336 | |
---|
1337 | static void |
---|
1338 | mixer_start_gmix_cb (BonoboUIComponent *uic, |
---|
1339 | MixerData *data, |
---|
1340 | const gchar *verbname) |
---|
1341 | { |
---|
1342 | GError *error = NULL; |
---|
1343 | |
---|
1344 | if (!run_mixer_cmd) |
---|
1345 | return; |
---|
1346 | |
---|
1347 | gdk_spawn_command_line_on_screen ( |
---|
1348 | gtk_widget_get_screen (GTK_WIDGET (data->applet)), |
---|
1349 | run_mixer_cmd, |
---|
1350 | &error); |
---|
1351 | |
---|
1352 | if (error) { |
---|
1353 | GtkWidget *dialog; |
---|
1354 | |
---|
1355 | dialog = gtk_message_dialog_new (NULL, |
---|
1356 | GTK_DIALOG_DESTROY_WITH_PARENT, |
---|
1357 | GTK_MESSAGE_ERROR, |
---|
1358 | GTK_BUTTONS_OK, |
---|
1359 | _("There was an error executing '%s': %s"), |
---|
1360 | run_mixer_cmd, |
---|
1361 | error->message); |
---|
1362 | |
---|
1363 | g_signal_connect (dialog, "response", |
---|
1364 | G_CALLBACK (gtk_widget_destroy), |
---|
1365 | NULL); |
---|
1366 | |
---|
1367 | gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); |
---|
1368 | gtk_window_set_screen (GTK_WINDOW (dialog), |
---|
1369 | gtk_widget_get_screen (data->applet)); |
---|
1370 | gtk_widget_show (dialog); |
---|
1371 | |
---|
1372 | g_error_free (error); |
---|
1373 | } |
---|
1374 | } |
---|
1375 | |
---|
1376 | static void |
---|
1377 | mixer_about_cb (BonoboUIComponent *uic, |
---|
1378 | MixerData *data, |
---|
1379 | const gchar *verbname) |
---|
1380 | { |
---|
1381 | GdkPixbuf *pixbuf = NULL; |
---|
1382 | gchar *file; |
---|
1383 | static const gchar *authors[] = { |
---|
1384 | "Richard Hult <richard@imendio.com>", |
---|
1385 | NULL |
---|
1386 | }; |
---|
1387 | |
---|
1388 | const gchar *documenters[] = { |
---|
1389 | NULL |
---|
1390 | }; |
---|
1391 | |
---|
1392 | const gchar *translator_credits = _("translator_credits"); |
---|
1393 | |
---|
1394 | if (data->about_dialog) { |
---|
1395 | gtk_window_set_screen (GTK_WINDOW (data->about_dialog), |
---|
1396 | gtk_widget_get_screen (data->applet)); |
---|
1397 | |
---|
1398 | gtk_window_present (GTK_WINDOW (data->about_dialog)); |
---|
1399 | return; |
---|
1400 | } |
---|
1401 | |
---|
1402 | file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, "gnome-mixer-applet.png", TRUE, NULL); |
---|
1403 | pixbuf = gdk_pixbuf_new_from_file (file, NULL); |
---|
1404 | g_free (file); |
---|
1405 | |
---|
1406 | data->about_dialog = gnome_about_new (_("Volume Control"), VERSION, |
---|
1407 | _("(C) 2001 Richard Hult"), |
---|
1408 | _("The volume control lets you set the volume level for your desktop."), |
---|
1409 | authors, |
---|
1410 | documenters, |
---|
1411 | strcmp (translator_credits, "translator_credits") != 0 ? translator_credits : NULL, |
---|
1412 | pixbuf); |
---|
1413 | |
---|
1414 | g_object_unref (pixbuf); |
---|
1415 | |
---|
1416 | gtk_window_set_screen (GTK_WINDOW (data->about_dialog), |
---|
1417 | gtk_widget_get_screen (data->applet)); |
---|
1418 | |
---|
1419 | gtk_window_set_wmclass (GTK_WINDOW(data->about_dialog), "volume control", "Volume Control"); |
---|
1420 | |
---|
1421 | gnome_window_icon_set_from_file (GTK_WINDOW (data->about_dialog), GNOME_ICONDIR "gnome-mixer-applet.png"); |
---|
1422 | |
---|
1423 | g_signal_connect (G_OBJECT (data->about_dialog), "destroy", |
---|
1424 | G_CALLBACK (gtk_widget_destroyed), |
---|
1425 | &data->about_dialog); |
---|
1426 | |
---|
1427 | gtk_widget_show (data->about_dialog); |
---|
1428 | } |
---|
1429 | |
---|
1430 | static void |
---|
1431 | mixer_help_cb (BonoboUIComponent *uic, |
---|
1432 | MixerData *data, |
---|
1433 | const gchar *verbname) |
---|
1434 | { |
---|
1435 | GError *error = NULL; |
---|
1436 | |
---|
1437 | gnome_help_display_on_screen ( |
---|
1438 | "mixer_applet2", NULL, |
---|
1439 | gtk_widget_get_screen (data->applet), |
---|
1440 | &error); |
---|
1441 | |
---|
1442 | if (error) { /* FIXME: the user needs to see this error */ |
---|
1443 | g_print ("%s \n", error->message); |
---|
1444 | g_error_free (error); |
---|
1445 | error = NULL; |
---|
1446 | } |
---|
1447 | } |
---|
1448 | |
---|
1449 | /* Dummy callback to get rid of a warning, for now. */ |
---|
1450 | static void |
---|
1451 | mixer_mute_cb (BonoboUIComponent *uic, |
---|
1452 | gpointer data, |
---|
1453 | const gchar *verbname) |
---|
1454 | { |
---|
1455 | } |
---|
1456 | |
---|
1457 | #ifdef ALLOW_PREFERENCES |
---|
1458 | |
---|
1459 | static gboolean |
---|
1460 | cb_row_selected (GtkTreeSelection *selection, MixerData *data) |
---|
1461 | { |
---|
1462 | PanelApplet *applet = PANEL_APPLET (data->applet); |
---|
1463 | GtkTreeIter iter; |
---|
1464 | GtkTreeModel *model; |
---|
1465 | ChannelData *cdata; |
---|
1466 | |
---|
1467 | if (!gtk_tree_selection_get_selected (selection, &model, &iter)) |
---|
1468 | return FALSE; |
---|
1469 | |
---|
1470 | gtk_tree_model_get (model, &iter, 1, &cdata, -1); |
---|
1471 | if (cdata->channel == data->mixerchannel) |
---|
1472 | return FALSE; |
---|
1473 | |
---|
1474 | data->mixerchannel = cdata->channel; |
---|
1475 | panel_applet_gconf_set_int (applet, "channel", data->mixerchannel, NULL); |
---|
1476 | } |
---|
1477 | |
---|
1478 | static void |
---|
1479 | show_help_cb (GtkWindow *dialog) |
---|
1480 | { |
---|
1481 | GError *error = NULL; |
---|
1482 | static GnomeProgram *applet_program = NULL; |
---|
1483 | |
---|
1484 | if (!applet_program) { |
---|
1485 | int argc = 1; |
---|
1486 | char *argv[2] = { "mixer_applet2" }; |
---|
1487 | applet_program = gnome_program_init ("mixer_applet2", VERSION, |
---|
1488 | LIBGNOME_MODULE, argc, argv, |
---|
1489 | GNOME_PROGRAM_STANDARD_PROPERTIES, NULL); |
---|
1490 | } |
---|
1491 | |
---|
1492 | gnome_help_display_desktop_on_screen ( |
---|
1493 | applet_program, "mixer_applet2", "mixer_applet2", NULL, |
---|
1494 | gtk_widget_get_screen (GTK_WIDGET (dialog)), |
---|
1495 | &error); |
---|
1496 | |
---|
1497 | if (error) { |
---|
1498 | GtkWidget *error_dialog; |
---|
1499 | |
---|
1500 | error_dialog = gtk_message_dialog_new ( |
---|
1501 | NULL, |
---|
1502 | GTK_DIALOG_DESTROY_WITH_PARENT, |
---|
1503 | GTK_MESSAGE_ERROR, |
---|
1504 | GTK_BUTTONS_OK, |
---|
1505 | _("There was an error displaying help: %s"), |
---|
1506 | error->message); |
---|
1507 | |
---|
1508 | g_signal_connect (error_dialog, "response", |
---|
1509 | G_CALLBACK (gtk_widget_destroy), |
---|
1510 | NULL); |
---|
1511 | |
---|
1512 | gtk_window_set_resizable (GTK_WINDOW (error_dialog), FALSE); |
---|
1513 | gtk_window_set_screen (GTK_WINDOW (error_dialog), |
---|
1514 | gtk_widget_get_screen (GTK_WIDGET (dialog))); |
---|
1515 | gtk_widget_show (error_dialog); |
---|
1516 | g_error_free (error); |
---|
1517 | } |
---|
1518 | } |
---|
1519 | |
---|
1520 | static void |
---|
1521 | dialog_response (GtkDialog *dialog, gint id, MixerData *data) |
---|
1522 | { |
---|
1523 | if (id == GTK_RESPONSE_HELP) { |
---|
1524 | show_help_cb (GTK_WINDOW (dialog)); |
---|
1525 | return; |
---|
1526 | } |
---|
1527 | |
---|
1528 | gtk_widget_destroy (GTK_WIDGET (dialog)); |
---|
1529 | data->prefdialog = NULL; |
---|
1530 | |
---|
1531 | } |
---|
1532 | |
---|
1533 | static void |
---|
1534 | mixer_pref_cb (BonoboUIComponent *uic, |
---|
1535 | MixerData *data, |
---|
1536 | const gchar *verbname) |
---|
1537 | { |
---|
1538 | GtkWidget *dialog; |
---|
1539 | GtkWidget *vbox, *vbox1, *vbox2, *vbox3; |
---|
1540 | GtkWidget *hbox; |
---|
1541 | GtkWidget *label; |
---|
1542 | GtkWidget *tree; |
---|
1543 | GtkListStore *model; |
---|
1544 | GtkWidget *scrolled; |
---|
1545 | GtkTreeViewColumn *column; |
---|
1546 | GtkCellRenderer *cell; |
---|
1547 | gchar *tmp; |
---|
1548 | GList *list = data->channels; |
---|
1549 | |
---|
1550 | if (data->prefdialog) { |
---|
1551 | gtk_window_present (GTK_WINDOW (data->prefdialog)); |
---|
1552 | return; |
---|
1553 | } |
---|
1554 | |
---|
1555 | dialog = gtk_dialog_new_with_buttons (_("Volume Control Preferences"), NULL, |
---|
1556 | GTK_DIALOG_DESTROY_WITH_PARENT, |
---|
1557 | GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, |
---|
1558 | GTK_STOCK_HELP, GTK_RESPONSE_HELP, |
---|
1559 | NULL); |
---|
1560 | gnome_window_icon_set_from_file (GTK_WINDOW (dialog), GNOME_ICONDIR "gnome-mixer-applet.png"); |
---|
1561 | gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); |
---|
1562 | gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); |
---|
1563 | gtk_window_set_screen (GTK_WINDOW (dialog), |
---|
1564 | gtk_widget_get_screen (GTK_WIDGET (data->applet))); |
---|
1565 | gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); |
---|
1566 | gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 300); |
---|
1567 | data->prefdialog = dialog; |
---|
1568 | |
---|
1569 | vbox = GTK_DIALOG (dialog)->vbox; |
---|
1570 | gtk_box_set_spacing (GTK_BOX (vbox), 2); |
---|
1571 | |
---|
1572 | vbox1 = gtk_vbox_new (FALSE, 18); |
---|
1573 | gtk_container_set_border_width (GTK_CONTAINER (vbox1), 5); |
---|
1574 | gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0); |
---|
1575 | |
---|
1576 | vbox2 = gtk_vbox_new (FALSE, 6); |
---|
1577 | gtk_box_pack_start (GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0); |
---|
1578 | |
---|
1579 | tmp = g_strdup_printf ("<b>%s</b>", _("Audio Channels")); |
---|
1580 | label = gtk_label_new (NULL); |
---|
1581 | gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); |
---|
1582 | gtk_label_set_markup (GTK_LABEL (label), tmp); |
---|
1583 | g_free (tmp); |
---|
1584 | gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0); |
---|
1585 | |
---|
1586 | hbox = gtk_hbox_new (FALSE, 0); |
---|
1587 | gtk_box_pack_start (GTK_BOX (vbox2), hbox, TRUE, TRUE, 0); |
---|
1588 | |
---|
1589 | label = gtk_label_new (" "); |
---|
1590 | gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); |
---|
1591 | |
---|
1592 | vbox3 = gtk_vbox_new (FALSE, 6); |
---|
1593 | gtk_box_pack_start (GTK_BOX (hbox), vbox3, TRUE, TRUE, 0); |
---|
1594 | |
---|
1595 | label = gtk_label_new_with_mnemonic (_("_Select channel to control:")); |
---|
1596 | gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); |
---|
1597 | gtk_box_pack_start (GTK_BOX (vbox3), label, FALSE, FALSE, 0); |
---|
1598 | |
---|
1599 | scrolled = gtk_scrolled_window_new(NULL,NULL); |
---|
1600 | gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), |
---|
1601 | GTK_SHADOW_IN); |
---|
1602 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), |
---|
1603 | GTK_POLICY_AUTOMATIC, |
---|
1604 | GTK_POLICY_AUTOMATIC); |
---|
1605 | gtk_box_pack_start (GTK_BOX (vbox3), scrolled, TRUE, TRUE, 0); |
---|
1606 | |
---|
1607 | model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); |
---|
1608 | tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model)); |
---|
1609 | gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree); |
---|
1610 | gtk_container_add (GTK_CONTAINER (scrolled), tree); |
---|
1611 | g_object_unref (G_OBJECT (model)); |
---|
1612 | cell = gtk_cell_renderer_text_new (); |
---|
1613 | column = gtk_tree_view_column_new_with_attributes ("hello", |
---|
1614 | cell, |
---|
1615 | "text", 0, |
---|
1616 | NULL); |
---|
1617 | gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); |
---|
1618 | gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE); |
---|
1619 | |
---|
1620 | while (list) { |
---|
1621 | ChannelData *cdata = list->data; |
---|
1622 | GtkTreeIter iter; |
---|
1623 | |
---|
1624 | gtk_list_store_append (GTK_LIST_STORE (model), &iter); |
---|
1625 | gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, _(cdata->name), 1, cdata, -1); |
---|
1626 | if (cdata->channel == data->mixerchannel) { |
---|
1627 | GtkTreePath *path; |
---|
1628 | path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); |
---|
1629 | gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree), path, NULL, FALSE); |
---|
1630 | } |
---|
1631 | list = g_list_next (list); |
---|
1632 | } |
---|
1633 | |
---|
1634 | if ( ! key_writable (PANEL_APPLET (data->applet), "channel")) { |
---|
1635 | gtk_widget_set_sensitive (tree, FALSE); |
---|
1636 | gtk_widget_set_sensitive (label, FALSE); |
---|
1637 | } |
---|
1638 | |
---|
1639 | g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree))), |
---|
1640 | "changed", |
---|
1641 | G_CALLBACK (cb_row_selected), data); |
---|
1642 | |
---|
1643 | g_signal_connect (G_OBJECT (dialog), "response", |
---|
1644 | G_CALLBACK (dialog_response), data); |
---|
1645 | |
---|
1646 | gtk_widget_show_all (dialog); |
---|
1647 | |
---|
1648 | } |
---|
1649 | #endif |
---|
1650 | |
---|
1651 | static void |
---|
1652 | mixer_ui_component_event (BonoboUIComponent *comp, |
---|
1653 | const gchar *path, |
---|
1654 | Bonobo_UIComponent_EventType type, |
---|
1655 | const gchar *state_string, |
---|
1656 | MixerData *data) |
---|
1657 | { |
---|
1658 | gboolean state; |
---|
1659 | |
---|
1660 | if (!strcmp (path, "Mute")) { |
---|
1661 | if (!strcmp (state_string, "1")) { |
---|
1662 | state = TRUE; |
---|
1663 | } else { |
---|
1664 | state = FALSE; |
---|
1665 | } |
---|
1666 | |
---|
1667 | if (state == data->mute) { |
---|
1668 | return; |
---|
1669 | } |
---|
1670 | |
---|
1671 | data->mute = state; |
---|
1672 | |
---|
1673 | if (data->mute) { |
---|
1674 | #ifdef SUN_API |
---|
1675 | set_mute_status (TRUE); |
---|
1676 | #else |
---|
1677 | setMixer (0, data); |
---|
1678 | #endif |
---|
1679 | mixer_update_image (data); |
---|
1680 | |
---|
1681 | |
---|
1682 | gtk_tooltips_set_tip (data->tooltips, |
---|
1683 | data->applet, |
---|
1684 | _(access_name_mute), |
---|
1685 | NULL); |
---|
1686 | if (gail_loaded) { |
---|
1687 | add_atk_namedesc (data->applet, |
---|
1688 | _(access_name_mute), |
---|
1689 | NULL); |
---|
1690 | } |
---|
1691 | } |
---|
1692 | else { |
---|
1693 | #ifdef SUN_API |
---|
1694 | set_mute_status (FALSE); |
---|
1695 | #else |
---|
1696 | setMixer (data->vol, data); |
---|
1697 | #endif |
---|
1698 | mixer_update_image (data); |
---|
1699 | |
---|
1700 | |
---|
1701 | gtk_tooltips_set_tip (data->tooltips, |
---|
1702 | data->applet, |
---|
1703 | _(access_name), |
---|
1704 | NULL); |
---|
1705 | if (gail_loaded) { |
---|
1706 | add_atk_namedesc (data->applet, |
---|
1707 | _(access_name), |
---|
1708 | NULL); |
---|
1709 | } |
---|
1710 | } |
---|
1711 | } |
---|
1712 | } |
---|
1713 | |
---|
1714 | static void |
---|
1715 | get_channel (MixerData *data) |
---|
1716 | { |
---|
1717 | PanelApplet *applet = PANEL_APPLET (data->applet); |
---|
1718 | int num; |
---|
1719 | |
---|
1720 | num = panel_applet_gconf_get_int (applet, "channel", NULL); |
---|
1721 | num = CLAMP (num, 0, g_list_length (data->channels) - 1); |
---|
1722 | data->mixerchannel = num; |
---|
1723 | } |
---|
1724 | |
---|
1725 | static void |
---|
1726 | get_prefs (MixerData *data) |
---|
1727 | { |
---|
1728 | PanelApplet *applet = PANEL_APPLET (data->applet); |
---|
1729 | gboolean mute; |
---|
1730 | |
---|
1731 | data->vol = panel_applet_gconf_get_int (applet, "vol", NULL); |
---|
1732 | mute = panel_applet_gconf_get_bool (applet, "mute", NULL); |
---|
1733 | |
---|
1734 | if (data->vol < 0) |
---|
1735 | return; |
---|
1736 | |
---|
1737 | data->mute = mute; |
---|
1738 | if (data->mute) { |
---|
1739 | #ifdef SUN_API |
---|
1740 | set_mute_status (TRUE); |
---|
1741 | #else |
---|
1742 | setMixer (0, data); |
---|
1743 | #endif |
---|
1744 | mixer_update_image (data); |
---|
1745 | |
---|
1746 | |
---|
1747 | gtk_tooltips_set_tip (data->tooltips, |
---|
1748 | data->applet, |
---|
1749 | _(access_name_mute), |
---|
1750 | NULL); |
---|
1751 | if (gail_loaded) { |
---|
1752 | add_atk_namedesc (data->applet, |
---|
1753 | _(access_name_mute), |
---|
1754 | NULL); |
---|
1755 | } |
---|
1756 | } |
---|
1757 | else { |
---|
1758 | #ifdef SUN_API |
---|
1759 | set_mute_status (FALSE); |
---|
1760 | #else |
---|
1761 | setMixer (data->vol, data); |
---|
1762 | #endif |
---|
1763 | mixer_update_image (data); |
---|
1764 | |
---|
1765 | |
---|
1766 | gtk_tooltips_set_tip (data->tooltips, |
---|
1767 | data->applet, |
---|
1768 | _(access_name), |
---|
1769 | NULL); |
---|
1770 | if (gail_loaded) { |
---|
1771 | add_atk_namedesc (data->applet, |
---|
1772 | _(access_name), |
---|
1773 | NULL); |
---|
1774 | } |
---|
1775 | } |
---|
1776 | } |
---|
1777 | |
---|
1778 | typedef struct |
---|
1779 | { |
---|
1780 | char *stock_id; |
---|
1781 | const guint8 *icon_data; |
---|
1782 | } MixerStockIcon; |
---|
1783 | |
---|
1784 | static MixerStockIcon items[] = |
---|
1785 | { |
---|
1786 | { VOLUME_STOCK_MUTE, GNOME_ICONDIR"/mixer/volume-mute.png" }, |
---|
1787 | { VOLUME_STOCK_ZERO, GNOME_ICONDIR"/mixer/volume-zero.png" }, |
---|
1788 | { VOLUME_STOCK_MIN, GNOME_ICONDIR"/mixer/volume-min.png" }, |
---|
1789 | { VOLUME_STOCK_MED, GNOME_ICONDIR"/mixer/volume-medium.png" }, |
---|
1790 | { VOLUME_STOCK_MAX, GNOME_ICONDIR"/mixer/volume-max.png" } |
---|
1791 | }; |
---|
1792 | |
---|
1793 | static void |
---|
1794 | register_mixer_stock_icons (GtkIconFactory *factory) |
---|
1795 | { |
---|
1796 | GtkIconSource *source; |
---|
1797 | int i; |
---|
1798 | |
---|
1799 | source = gtk_icon_source_new (); |
---|
1800 | |
---|
1801 | for (i = 0; i < G_N_ELEMENTS (items); i++) { |
---|
1802 | GtkIconSet *icon_set; |
---|
1803 | |
---|
1804 | gtk_icon_source_set_filename (source, items[i].icon_data); |
---|
1805 | |
---|
1806 | icon_set = gtk_icon_set_new (); |
---|
1807 | gtk_icon_set_add_source (icon_set, source); |
---|
1808 | |
---|
1809 | gtk_icon_factory_add (factory, items[i].stock_id, icon_set); |
---|
1810 | |
---|
1811 | gtk_icon_set_unref (icon_set); |
---|
1812 | } |
---|
1813 | gtk_icon_source_free (source); |
---|
1814 | } |
---|
1815 | |
---|
1816 | static void |
---|
1817 | mixer_init_stock_icons (void) |
---|
1818 | { |
---|
1819 | GtkIconFactory *factory; |
---|
1820 | |
---|
1821 | if (icons_initialized) |
---|
1822 | return; |
---|
1823 | |
---|
1824 | volume_icon_size = gtk_icon_size_register ("panel-menu", |
---|
1825 | VOLUME_DEFAULT_ICON_SIZE, |
---|
1826 | VOLUME_DEFAULT_ICON_SIZE); |
---|
1827 | |
---|
1828 | factory = gtk_icon_factory_new (); |
---|
1829 | gtk_icon_factory_add_default (factory); |
---|
1830 | |
---|
1831 | register_mixer_stock_icons (factory); |
---|
1832 | |
---|
1833 | g_object_unref (factory); |
---|
1834 | |
---|
1835 | icons_initialized = TRUE; |
---|
1836 | } |
---|
1837 | |
---|
1838 | const BonoboUIVerb mixer_applet_menu_verbs [] = { |
---|
1839 | BONOBO_UI_UNSAFE_VERB ("RunMixer", mixer_start_gmix_cb), |
---|
1840 | BONOBO_UI_UNSAFE_VERB ("Mute", mixer_mute_cb), |
---|
1841 | BONOBO_UI_UNSAFE_VERB ("Help", mixer_help_cb), |
---|
1842 | BONOBO_UI_UNSAFE_VERB ("About", mixer_about_cb), |
---|
1843 | #ifdef ALLOW_PREFERENCES |
---|
1844 | BONOBO_UI_UNSAFE_VERB ("Pref", mixer_pref_cb), |
---|
1845 | #endif |
---|
1846 | BONOBO_UI_VERB_END |
---|
1847 | }; |
---|
1848 | |
---|
1849 | static void |
---|
1850 | applet_style_event_cb (GtkWidget *w, GtkStyle *prev_style, MixerData *user_data) |
---|
1851 | { |
---|
1852 | mixer_load_volume_images (user_data); |
---|
1853 | mixer_update_image (user_data); |
---|
1854 | } |
---|
1855 | |
---|
1856 | mixer_applet_create (PanelApplet *applet) |
---|
1857 | { |
---|
1858 | MixerData *data; |
---|
1859 | BonoboUIComponent *component; |
---|
1860 | const gchar *device = NULL; |
---|
1861 | gboolean retval; |
---|
1862 | #ifdef SUN_API |
---|
1863 | gchar *ctl = NULL; |
---|
1864 | #endif |
---|
1865 | |
---|
1866 | data = g_new0 (MixerData, 1); |
---|
1867 | data->applet = GTK_WIDGET (applet); |
---|
1868 | panel_applet_add_preferences (applet, "/schemas/apps/mixer_applet/prefs", NULL); |
---|
1869 | panel_applet_set_flags (applet, PANEL_APPLET_EXPAND_MINOR); |
---|
1870 | get_channel (data); |
---|
1871 | |
---|
1872 | #ifdef OSS_API |
---|
1873 | /* /dev/sound/mixer for devfs */ |
---|
1874 | device = "/dev/mixer"; |
---|
1875 | retval = openMixer(device, data); |
---|
1876 | if (!retval) { |
---|
1877 | device = "/dev/sound/mixer"; |
---|
1878 | retval = openMixer (device, data); |
---|
1879 | } |
---|
1880 | #endif |
---|
1881 | #ifdef SUN_API |
---|
1882 | if (!(ctl = g_getenv("AUDIODEV"))) |
---|
1883 | ctl = "/dev/audio"; |
---|
1884 | device = g_strdup_printf("%sctl",ctl); |
---|
1885 | retval = openMixer(device, data); |
---|
1886 | #endif |
---|
1887 | #ifdef AIX_API |
---|
1888 | if (!(device = g_getenv("AUDIODEV"))) |
---|
1889 | device = "/dev/paud0"; |
---|
1890 | retval = openMixer(device, data); |
---|
1891 | #endif |
---|
1892 | #ifdef GSTREAMER_API |
---|
1893 | retval = gstreamer_init (data); |
---|
1894 | #endif |
---|
1895 | |
---|
1896 | mixer_init_stock_icons (); |
---|
1897 | if (!retval) { |
---|
1898 | device_present = FALSE; |
---|
1899 | } |
---|
1900 | |
---|
1901 | if (GTK_IS_ACCESSIBLE (gtk_widget_get_accessible(GTK_WIDGET(applet)))) { |
---|
1902 | gail_loaded = TRUE; |
---|
1903 | } |
---|
1904 | |
---|
1905 | /* Try to find some mixers - sdtaudiocontrol is on Solaris and is needed |
---|
1906 | ** because gnome-volume-meter doesn't work */ |
---|
1907 | if (run_mixer_cmd == NULL) |
---|
1908 | run_mixer_cmd = g_find_program_in_path ("gnome-volume-control"); |
---|
1909 | if (run_mixer_cmd == NULL) |
---|
1910 | run_mixer_cmd = g_find_program_in_path ("gmix"); |
---|
1911 | if (run_mixer_cmd == NULL) |
---|
1912 | run_mixer_cmd = g_find_program_in_path ("sdtaudiocontrol"); |
---|
1913 | |
---|
1914 | data->device = g_strdup (device); |
---|
1915 | data->frame = gtk_frame_new (NULL); |
---|
1916 | gtk_frame_set_shadow_type (GTK_FRAME (data->frame), GTK_SHADOW_NONE); |
---|
1917 | gtk_container_add (GTK_CONTAINER (applet), data->frame); |
---|
1918 | |
---|
1919 | data->image = gtk_image_new (); |
---|
1920 | gtk_container_add (GTK_CONTAINER (data->frame), data->image); |
---|
1921 | |
---|
1922 | mixer_load_volume_images (data); |
---|
1923 | |
---|
1924 | data->about_dialog = NULL; |
---|
1925 | |
---|
1926 | data->tooltips = gtk_tooltips_new (); |
---|
1927 | g_object_ref (data->tooltips); |
---|
1928 | gtk_object_sink (GTK_OBJECT (data->tooltips)); |
---|
1929 | gtk_tooltips_set_tip (data->tooltips, |
---|
1930 | data->applet, |
---|
1931 | _(access_name), |
---|
1932 | NULL); |
---|
1933 | if (gail_loaded) { |
---|
1934 | add_atk_namedesc(GTK_WIDGET(data->applet), |
---|
1935 | _(access_name), |
---|
1936 | _("The volume control lets you set the volume level for your desktop")); |
---|
1937 | } |
---|
1938 | |
---|
1939 | g_signal_connect (data->applet, |
---|
1940 | "button-press-event", |
---|
1941 | (GCallback) applet_button_release_event_cb, |
---|
1942 | data); |
---|
1943 | |
---|
1944 | g_signal_connect (data->applet, |
---|
1945 | "key-press-event", |
---|
1946 | (GCallback) applet_key_press_event_cb, |
---|
1947 | data); |
---|
1948 | |
---|
1949 | g_signal_connect (data->applet, |
---|
1950 | "scroll-event", |
---|
1951 | (GCallback) applet_scroll_event_cb, |
---|
1952 | data); |
---|
1953 | |
---|
1954 | g_signal_connect (data->applet, |
---|
1955 | "style-set", |
---|
1956 | (GCallback) applet_style_event_cb, |
---|
1957 | data); |
---|
1958 | |
---|
1959 | data->adj = GTK_ADJUSTMENT ( |
---|
1960 | gtk_adjustment_new (50, |
---|
1961 | 0.0, |
---|
1962 | VOLUME_MAX, |
---|
1963 | VOLUME_MAX/20, |
---|
1964 | VOLUME_MAX/10, |
---|
1965 | 0.0)); |
---|
1966 | |
---|
1967 | g_signal_connect (data->adj, |
---|
1968 | "value-changed", |
---|
1969 | (GCallback) mixer_value_changed_cb, |
---|
1970 | data); |
---|
1971 | |
---|
1972 | /* Get the initial volume. */ |
---|
1973 | get_prefs (data); |
---|
1974 | |
---|
1975 | if (!data->mute) |
---|
1976 | data->vol = readMixer (data); |
---|
1977 | |
---|
1978 | /* Install timeout handler, that keeps the applet up-to-date if the |
---|
1979 | * volume is changed somewhere else. |
---|
1980 | */ |
---|
1981 | data->timeout = g_timeout_add (500, |
---|
1982 | (GSourceFunc) mixer_timeout_cb, |
---|
1983 | data); |
---|
1984 | |
---|
1985 | g_signal_connect (applet, |
---|
1986 | "destroy", |
---|
1987 | (GCallback) destroy_mixer_cb, |
---|
1988 | data); |
---|
1989 | |
---|
1990 | g_signal_connect (applet, |
---|
1991 | "change_orient", |
---|
1992 | G_CALLBACK (applet_change_orient_cb), |
---|
1993 | data); |
---|
1994 | |
---|
1995 | g_signal_connect (applet, |
---|
1996 | "change_size", |
---|
1997 | G_CALLBACK (applet_change_size_cb), |
---|
1998 | data); |
---|
1999 | |
---|
2000 | g_signal_connect (applet, |
---|
2001 | "change_background", |
---|
2002 | G_CALLBACK (applet_change_background_cb), |
---|
2003 | data); |
---|
2004 | |
---|
2005 | g_signal_connect (applet, |
---|
2006 | "size_allocate", |
---|
2007 | G_CALLBACK (applet_size_allocate), |
---|
2008 | data); |
---|
2009 | |
---|
2010 | panel_applet_setup_menu_from_file (applet, |
---|
2011 | NULL, /* opt_datadir */ |
---|
2012 | "GNOME_MixerApplet.xml", |
---|
2013 | NULL, |
---|
2014 | mixer_applet_menu_verbs, |
---|
2015 | data); |
---|
2016 | |
---|
2017 | if (panel_applet_get_locked_down (applet)) { |
---|
2018 | BonoboUIComponent *popup_component; |
---|
2019 | |
---|
2020 | popup_component = panel_applet_get_popup_component (applet); |
---|
2021 | |
---|
2022 | bonobo_ui_component_set_prop (popup_component, |
---|
2023 | "/commands/Pref", |
---|
2024 | "hidden", "1", |
---|
2025 | NULL); |
---|
2026 | } |
---|
2027 | |
---|
2028 | |
---|
2029 | component = panel_applet_get_popup_component (applet); |
---|
2030 | g_signal_connect (component, |
---|
2031 | "ui-event", |
---|
2032 | (GCallback) mixer_ui_component_event, |
---|
2033 | data); |
---|
2034 | if (data->mute) |
---|
2035 | bonobo_ui_component_set_prop (component, |
---|
2036 | "/commands/Mute", |
---|
2037 | "state", |
---|
2038 | "1", |
---|
2039 | NULL); |
---|
2040 | else |
---|
2041 | bonobo_ui_component_set_prop (component, |
---|
2042 | "/commands/Mute", |
---|
2043 | "state", |
---|
2044 | "0", |
---|
2045 | NULL); |
---|
2046 | |
---|
2047 | if (run_mixer_cmd == NULL) |
---|
2048 | bonobo_ui_component_rm (component, "/popups/popup/RunMixer", NULL); |
---|
2049 | |
---|
2050 | #ifndef ALLOW_PREFERENCES |
---|
2051 | bonobo_ui_component_rm (component, "/popups/popup/Pref", NULL); |
---|
2052 | #endif |
---|
2053 | |
---|
2054 | applet_change_orient_cb (GTK_WIDGET (applet), |
---|
2055 | panel_applet_get_orient (applet), |
---|
2056 | data); |
---|
2057 | applet_change_size_cb (GTK_WIDGET (applet), |
---|
2058 | panel_applet_get_size (applet), |
---|
2059 | data); |
---|
2060 | |
---|
2061 | mixer_update_slider (data); |
---|
2062 | mixer_update_image (data); |
---|
2063 | |
---|
2064 | gtk_widget_show_all (GTK_WIDGET (applet)); |
---|
2065 | } |
---|
2066 | |
---|
2067 | static gboolean |
---|
2068 | mixer_applet_factory (PanelApplet *applet, |
---|
2069 | const gchar *iid, |
---|
2070 | gpointer data) |
---|
2071 | { |
---|
2072 | mixer_applet_create (applet); |
---|
2073 | |
---|
2074 | return TRUE; |
---|
2075 | } |
---|
2076 | |
---|
2077 | PANEL_APPLET_BONOBO_FACTORY ("OAFIID:GNOME_MixerApplet_Factory", |
---|
2078 | PANEL_TYPE_APPLET, |
---|
2079 | "mixer_applet2", |
---|
2080 | "0", |
---|
2081 | mixer_applet_factory, |
---|
2082 | NULL) |
---|
2083 | |
---|
2084 | /* Accessible name and description */ |
---|
2085 | void |
---|
2086 | add_atk_namedesc (GtkWidget *widget, const gchar *name, const gchar *desc) |
---|
2087 | { |
---|
2088 | AtkObject *atk_widget; |
---|
2089 | atk_widget = gtk_widget_get_accessible(widget); |
---|
2090 | atk_object_set_name(atk_widget, name); |
---|
2091 | atk_object_set_description(atk_widget, desc); |
---|
2092 | } |
---|