2 volume.py (a volume control applet for the ROX Panel)
4 Copyright 2004 Kenneth Hayber <ken@hayber.us>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License.
11 This program is distributed in the hope that it will be useful
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 from rox
import app_options
, applet
, Menu
, InfoWin
23 from rox
.options
import Option
24 from volumecontrol
import VolumeControl
29 rox
.croak(_("You need python 2.3 for ossaudiodev support"))
35 #Options.xml processing
36 from rox
import choices
37 choices
.migrate(APP_NAME
, 'hayber.us')
38 rox
.setup_app_options(APP_NAME
, site
='hayber.us')
39 Menu
.set_save_name(APP_NAME
, site
='hayber.us')
41 MIXER_DEVICE
= Option('mixer_device', '/dev/mixer')
42 VOLUME_CONTROL
= Option('volume_control', 'VOLUME')
43 SHOW_ICON
= Option('show_icon', True)
44 SHOW_BAR
= Option('show_bar', False)
46 rox
.app_options
.notify()
49 'VOLUME':ossaudiodev
.SOUND_MIXER_VOLUME
,
50 'PCM':ossaudiodev
.SOUND_MIXER_PCM
,
51 'Other':ossaudiodev
.SOUND_MIXER_ALTPCM
54 class Volume(applet
.Applet
):
55 """An applet to control a sound card Master or PCM volume"""
56 def __init__(self
, filename
):
57 applet
.Applet
.__init
__(self
, filename
)
59 self
.vertical
= self
.get_panel_orientation() in ('Right', 'Left')
61 self
.set_size_request(8, -1)
63 bar_orient
= gtk
.PROGRESS_LEFT_TO_RIGHT
65 self
.set_size_request(-1, 8)
67 bar_orient
= gtk
.PROGRESS_BOTTOM_TO_TOP
71 self
.image
= gtk
.Image()
72 self
.pixbuf
= gtk
.gdk
.pixbuf_new_from_file(APP_DIR
+'/images/volume.svg')
73 self
.image
.set_from_pixbuf(self
.pixbuf
)
75 self
.box
.pack_start(self
.image
)
77 self
.bar
= gtk
.ProgressBar()
78 self
.bar
.set_orientation(bar_orient
)
79 self
.bar
.set_size_request(12,12)
80 self
.box
.pack_end(self
.bar
)
82 tooltips
= gtk
.Tooltips()
83 tooltips
.set_tip(self
, _('Volume control'), tip_private
=None)
85 rox
.app_options
.add_notify(self
.get_options
)
86 self
.connect('size-allocate', self
.event_callback
)
87 self
.connect('scroll_event', self
.button_scroll
)
89 self
.add_events(gtk
.gdk
.BUTTON_PRESS_MASK
)
90 self
.connect('button-press-event', self
.button_press
)
91 self
.menu
= Menu
.Menu('main', [
92 Menu
.Action(_('Mixer'), 'run_mixer', ''),
94 Menu
.Action(_('Options'), 'show_options', '', gtk
.STOCK_PREFERENCES
),
95 Menu
.Action(_('Info'), 'get_info', '', gtk
.STOCK_DIALOG_INFO
),
96 Menu
.Action(_('Close'), 'quit', '', gtk
.STOCK_CLOSE
),
98 self
.menu
.attach(self
, self
)
101 self
.mixer
= ossaudiodev
.openmixer(MIXER_DEVICE
.value
)
102 self
.get_volume(MIXER_CONTROLS
[VOLUME_CONTROL
.value
])
106 if not SHOW_ICON
.int_value
:
108 if not SHOW_BAR
.int_value
:
113 def button_scroll(self
, window
, event
):
114 channel
= MIXER_CONTROLS
[VOLUME_CONTROL
.value
]
115 current_volume
= self
.get_volume(channel
)
116 if event
.direction
== 0:
117 self
.set_volume((current_volume
[0]+2, current_volume
[1]+2), channel
)
118 elif event
.direction
== 1:
119 self
.set_volume((current_volume
[0]-2, current_volume
[1]-2), channel
)
121 def event_callback(self
, widget
, rectangle
):
122 """Called when the panel sends a size."""
127 if size
!= self
.size
:
128 self
.resize_image(size
)
130 def resize_image(self
, size
):
131 """Called to resize the image."""
132 #I like the look better with the -4, there is no technical reason for it.
133 scaled_pixbuf
= self
.pixbuf
.scale_simple(size
-4, size
-4, gtk
.gdk
.INTERP_BILINEAR
)
134 self
.image
.set_from_pixbuf(scaled_pixbuf
)
137 def button_press(self
, window
, event
):
138 """Show/Hide the volume control on button 1 and the menu on button 3"""
139 if event
.button
== 1:
140 if not self
.hide_volume():
141 self
.show_volume(event
)
142 elif event
.button
== 3:
144 self
.menu
.popup(self
, event
, self
.position_menu
)
146 def hide_volume(self
, event
=None):
147 """Destroy the popup volume control"""
155 def get_panel_orientation(self
):
156 """Return the panel orientation ('Top', 'Bottom', 'Left', 'Right')
157 and the margin for displaying a popup menu"""
158 pos
= self
.socket
.property_get('_ROX_PANEL_MENU_POS', 'STRING', False)
161 side
, margin
= pos
.split(',')
164 side
, margin
= None, 2
167 def set_position(self
):
168 """Set the position of the popup"""
169 side
= self
.get_panel_orientation()
171 # widget (x, y, w, h, bits)
172 geometry
= self
.socket
.get_geometry()
176 self
.thing
.set_size_request(APP_SIZE
[0], APP_SIZE
[1])
177 self
.thing
.move(self
.socket
.get_origin()[0],
178 self
.socket
.get_origin()[1]-APP_SIZE
[1])
181 self
.thing
.set_size_request(APP_SIZE
[0], APP_SIZE
[1])
182 self
.thing
.move(self
.socket
.get_origin()[0],
183 self
.socket
.get_origin()[1]+geometry
[3])
186 self
.thing
.set_size_request(APP_SIZE
[1], APP_SIZE
[0])
187 self
.thing
.move(self
.socket
.get_origin()[0]+geometry
[2],
188 self
.socket
.get_origin()[1])
189 elif side
== 'Right':
191 self
.thing
.set_size_request(APP_SIZE
[1], APP_SIZE
[0])
192 self
.thing
.move(self
.socket
.get_origin()[0]-APP_SIZE
[1],
193 self
.socket
.get_origin()[1])
196 def show_volume(self
, event
):
197 """Display the popup volume control"""
198 # self.mixer = ossaudiodev.openmixer(MIXER_DEVICE.value)
200 self
.thing
= gtk
.Window(type=gtk
.WINDOW_POPUP
)
201 self
.thing
.set_type_hint(gtk
.gdk
.WINDOW_TYPE_HINT_MENU
)
202 self
.thing
.set_decorated(False)
204 vertical
= self
.set_position()
205 self
.volume
= VolumeControl(MIXER_CONTROLS
[VOLUME_CONTROL
.value
],
206 0, 0, True, None, vertical
)
207 self
.volume
.set_level(self
.get_volume(MIXER_CONTROLS
[VOLUME_CONTROL
.value
]))
208 self
.volume
.connect("volume_changed", self
.adjust_volume
)
210 self
.thing
.add(self
.volume
)
211 self
.thing
.show_all()
214 def adjust_volume(self
, vol
, channel
, vol_left
, vol_right
):
215 """Set the playback volume"""
216 self
.set_volume((vol_left
, vol_right
), channel
)
218 def set_volume(self
, volume
, channel
):
219 """Send the volume setting(s) to the mixer """
220 self
.mixer
.set(channel
, volume
)
221 self
.bar
.set_fraction(max(volume
[0], volume
[1])/100.0)
223 def get_volume(self
, channel
):
224 """Get the volume settings from the mixer"""
225 vol
= self
.mixer
.get(channel
)
226 self
.bar
.set_fraction(max(vol
[0], vol
[1])/100.0)
227 return (vol
[0], vol
[1])
229 def get_options(self
):
230 """Used as the notify callback when options change"""
231 if VOLUME_CONTROL
.has_changed
:
232 self
.get_volume(MIXER_CONTROLS
[VOLUME_CONTROL
.value
])
234 if SHOW_BAR
.has_changed
:
235 if SHOW_BAR
.int_value
:
240 if SHOW_ICON
.has_changed
:
241 if SHOW_ICON
.int_value
:
246 def show_options(self
, button
=None):
247 """Options edit dialog"""
251 """Display an InfoWin box"""
252 InfoWin
.infowin(APP_NAME
)
254 def run_mixer(self
, button
=None):
255 from rox
import filer
256 filer
.spawn_rox((APP_DIR
,))