Add jack_mix_box, a minimalistic jack mixer (no UI, controlled by MIDI)
[jack_mixer.git] / slider.py
blobd6f38b822116b94d0886e55617657e9a9bbc0f1d
1 # This file is part of jack_mixer
3 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
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; version 2 of the License
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 import gtk
19 import gobject
21 class AdjustmentdBFS(gtk.Adjustment):
22 def __init__(self, scale, default_db):
23 self.default_value = scale.db_to_scale(default_db)
24 self.db = default_db
25 self.scale = scale
26 gtk.Adjustment.__init__(self, self.default_value, 0.0, 1.0, 0.02)
27 self.connect("value-changed", self.on_value_changed)
28 self.disable_value_notify = False
30 def step_up(self):
31 self.set_value(self.get_value() + self.step_increment)
33 def step_down(self):
34 self.set_value(self.get_value() - self.step_increment)
36 def reset(self):
37 self.set_value(self.default_value)
39 def get_value_db(self):
40 return self.db
42 def set_value_db(self, db):
43 self.db = db
44 self.disable_value_notify = True
45 self.set_value(self.scale.db_to_scale(db))
46 self.disable_value_notify = False
47 self.emit("volume-changed")
49 def on_value_changed(self, adjustment):
50 if not self.disable_value_notify:
51 self.db = self.scale.scale_to_db(self.get_value())
52 self.emit("volume-changed")
54 def set_scale(self, scale):
55 self.scale = scale
56 self.disable_value_notify = True
57 self.set_value(self.scale.db_to_scale(self.db))
58 self.disable_value_notify = False
60 gobject.signal_new("volume-changed", AdjustmentdBFS,
61 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
64 class GtkSlider(gtk.VScale):
65 def __init__(self, adjustment):
66 gtk.VScale.__init__(self, adjustment)
67 self.set_draw_value(False)
68 self.set_inverted(True)
70 # HACK: we want the behaviour you get with the middle button, so we
71 # mangle the events. Clicking with other buttons moves the slider in
72 # step increments, clicking with the middle button moves the slider
73 # to the location of the click.
74 self.connect('button-press-event', self.button_press_event)
75 self.connect('button-release-event', self.button_release_event)
77 def button_press_event(self, widget, event):
78 event.button = 2
79 return False
81 def button_release_event(self, widget, event):
82 event.button = 2
83 return False
86 class CustomSliderWidget(gtk.DrawingArea):
87 def __init__(self, adjustment):
88 gtk.DrawingArea.__init__(self)
90 self.adjustment = adjustment
92 self.connect("expose-event", self.on_expose)
93 self.connect("size-request", self.on_size_request)
94 self.connect("size_allocate", self.on_size_allocate)
95 adjustment.connect("value-changed", self.on_value_changed)
96 self.connect("button-press-event", self.on_mouse)
97 self.connect("motion-notify-event", self.on_mouse)
98 self.set_events(gtk.gdk.BUTTON1_MOTION_MASK | gtk.gdk.BUTTON1_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK)
100 def on_mouse(self, widget, event):
101 if event.type == gtk.gdk.BUTTON_PRESS:
102 #print "mouse button %u pressed %u:%u" % (event.button, event.x, event.y)
103 if event.button == 1:
104 if event.y >= self.slider_rail_up and event.y < self.slider_rail_up + self.slider_rail_height:
105 self.adjustment.set_value(1 - float(event.y - self.slider_rail_up)/float(self.slider_rail_height))
106 elif event.button == 2:
107 self.adjustment.reset()
108 elif event.type == gtk.gdk.MOTION_NOTIFY:
109 #print "mouse motion %u:%u" % (event.x, event.y)
110 if event.y < self.slider_rail_up:
111 y = self.slider_rail_up
112 elif event.y > self.slider_rail_up + self.slider_rail_height:
113 y = self.slider_rail_up + self.slider_rail_height
114 else:
115 y = event.y
116 self.adjustment.set_value(1 - float(y - self.slider_rail_up)/float(self.slider_rail_height))
118 return False
120 def on_value_changed(self, adjustment):
121 self.invalidate_all()
123 def on_expose(self, widget, event):
124 cairo_ctx = widget.window.cairo_create()
126 # set a clip region for the expose event
127 cairo_ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
128 cairo_ctx.clip()
130 self.draw(cairo_ctx)
132 return False
134 def on_size_allocate(self, widget, allocation):
135 #print allocation.x, allocation.y, allocation.width, allocation.height
136 self.width = float(allocation.width)
137 self.height = float(allocation.height)
138 self.font_size = 10
140 def on_size_request(self, widget, requisition):
141 #print "size-request, %u x %u" % (requisition.width, requisition.height)
142 requisition.width = 20
143 return
145 def invalidate_all(self):
146 self.queue_draw_area(0, 0, int(self.width), int(self.height))
148 def draw(self, cairo_ctx):
149 if self.flags() & gtk.HAS_FOCUS:
150 state = gtk.STATE_PRELIGHT
151 else:
152 state = gtk.STATE_NORMAL
154 #cairo_ctx.rectangle(0, 0, self.width, self.height)
155 #cairo_ctx.set_source_color(self.style.bg[state])
156 #cairo_ctx.fill_preserve()
157 cairo_ctx.set_source_color(self.style.fg[state])
158 #cairo_ctx.stroke()
160 slider_knob_width = self.width * 3 / 4
161 slider_knob_height = self.width * 3 / 2
162 slider_knob_height -= slider_knob_height % 2
163 slider_knob_height += 1
165 slider_x = self.width/2
167 cairo_ctx.set_line_width(1)
169 # slider rail
170 cairo_ctx.set_source_color(self.style.dark[state])
171 self.slider_rail_up = slider_knob_height/2 + (self.width - slider_knob_width)/2
172 self.slider_rail_height = self.height - 2 * self.slider_rail_up
173 cairo_ctx.move_to(slider_x, self.slider_rail_up)
174 cairo_ctx.line_to(slider_x, self.slider_rail_height + self.slider_rail_up)
175 cairo_ctx.stroke()
177 # slider knob
178 slider_y = round(self.slider_rail_up + self.slider_rail_height * (1 - self.adjustment.get_value()))
179 cairo_ctx.rectangle(slider_x - float(slider_knob_width)/2,
180 slider_y - slider_knob_height/2,
181 float(slider_knob_width),
182 slider_knob_height)
183 cairo_ctx.set_source_color(self.style.bg[state])
184 cairo_ctx.fill_preserve()
185 cairo_ctx.set_source_color(self.style.fg[state])
186 cairo_ctx.stroke()
187 # slider knob marks
188 cairo_ctx.set_source_color(self.style.fg[state])
189 for i in range(int(slider_knob_height/2))[8:]:
190 if i % 2 == 0:
191 correction = 1.0 + (float(slider_knob_height)/2.0 - float(i)) / 10.0
192 correction *= 2
193 y = slider_y - i
194 w = float(slider_knob_width)/2.0 - correction
195 x1 = slider_x - w
196 x2 = slider_x + w
197 cairo_ctx.move_to(x1, y+0.5)
198 cairo_ctx.line_to(x2, y+0.5)
199 y = slider_y + i
200 cairo_ctx.move_to(x1, y-0.5)
201 cairo_ctx.line_to(x2, y-0.5)
202 cairo_ctx.set_line_width(1)
203 cairo_ctx.stroke()
204 # slider knob middle mark
205 cairo_ctx.move_to(slider_x - float(slider_knob_width)/2, slider_y)
206 cairo_ctx.line_to(slider_x + float(slider_knob_width)/2, slider_y)
207 cairo_ctx.set_line_width(2)
208 cairo_ctx.stroke()