]> git.0d.be Git - jack_mixer.git/blob - slider.py
Do pick up mode properly for volume
[jack_mixer.git] / slider.py
1 # This file is part of jack_mixer
2 #
3 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
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; version 2 of the License
8 #
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.
13 #
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.
17
18 from gi.repository import Gtk
19 from gi.repository import Gdk
20 from gi.repository import GObject
21 import cairo
22
23 class AdjustmentdBFS(Gtk.Adjustment):
24     def __init__(self, scale, default_db, step_inc):
25         self.default_value = scale.db_to_scale(default_db)
26         self.db = default_db
27         self.scale = scale
28         self.step_increment = step_inc
29         Gtk.Adjustment.__init__(self, self.default_value, 0.0, 1.0, 0.02)
30         self.connect("value-changed", self.on_value_changed)
31         self.disable_value_notify = False
32
33     def step_up(self):
34         self.set_value(self.get_value() + self.step_increment)
35
36     def step_down(self):
37         self.set_value(self.get_value() - self.step_increment)
38
39     def reset(self):
40         self.set_value(self.default_value)
41
42     def get_value_db(self):
43         return self.db
44
45     def set_value_db(self, db, from_midi = False):
46         self.db = db
47         self.disable_value_notify = True
48         self.set_value(self.scale.db_to_scale(db))
49         self.disable_value_notify = False
50         if not from_midi:
51             self.emit("volume-changed")
52         else:
53             self.emit("volume-changed-from-midi")
54
55     def on_value_changed(self, adjustment):
56         if not self.disable_value_notify:
57             self.db = self.scale.scale_to_db(self.get_value())
58             self.emit("volume-changed")
59
60     def set_scale(self, scale):
61         self.scale = scale
62         self.disable_value_notify = True
63         self.set_value(self.scale.db_to_scale(self.db))
64         self.disable_value_notify = False
65
66 GObject.signal_new("volume-changed", AdjustmentdBFS,
67                    GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, None, [])
68
69 GObject.signal_new("volume-changed-from-midi", AdjustmentdBFS,
70                    GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION, None, [])
71
72
73 class GtkSlider(Gtk.VScale):
74     def __init__(self, adjustment):
75         Gtk.VScale.__init__(self)#, adjustment)
76         self.adjustment = adjustment
77         self.set_adjustment(adjustment)
78         self.set_draw_value(False)
79         self.set_inverted(True)
80         self.button_down = False
81         self.button_down_y = 0
82         self.button_down_value = 0
83
84         # HACK: we want the behaviour you get with the middle button, so we
85         # mangle the events. Clicking with other buttons moves the slider in
86         # step increments, clicking with the middle button moves the slider
87         # to the location of the click.
88         self.connect('button-press-event', self.button_press_event)
89         self.connect('button-release-event', self.button_release_event)
90         self.connect('button-release-event', self.button_release_event)
91         self.connect("motion-notify-event", self.motion_notify_event)
92         self.connect("scroll-event", self.scroll_event)
93
94     def button_press_event(self, widget, event):
95         if event.type == Gdk.EventType.BUTTON_PRESS:
96             widget.get_style_context().set_state(Gtk.StateFlags.FOCUSED)
97             widget.get_style_context().set_state(Gtk.StateFlags.PRELIGHT|Gtk.StateFlags.FOCUSED)
98             widget.get_style_context().add_class('dragging')
99             widget.get_style_context().add_class(':focused')
100             self.button_down = True
101             self.button_down_y = event.y
102             self.button_down_value = self.adjustment.get_value()
103             return True
104         if not event.state & Gdk.ModifierType.CONTROL_MASK and event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS:
105             self.adjustment.set_value(0)
106             return True
107         if event.state & Gdk.ModifierType.CONTROL_MASK and event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS:
108             self.adjustment.set_value(self.adjustment.scale.db_to_scale(1))
109             return True
110         return False
111
112     def button_release_event(self, widget, event):
113         self.button_down = False
114         return False
115
116     def motion_notify_event(self, widget, event):
117         slider_length = widget.get_allocation().height - widget.get_style_context().get_property('min-height', Gtk.StateFlags.NORMAL)
118         if self.button_down:
119             delta_y = (self.button_down_y - event.y) / slider_length
120             y = self.button_down_value + delta_y
121             if y >= 1:
122                 y = 1
123             elif y <= 0:
124                 y = 0
125             self.adjustment.set_value(y)
126             return True
127
128     def scroll_event(self, widget, event):
129         delta = self.adjustment.step_increment
130         value = self.adjustment.get_value()
131         if event.direction == Gdk.ScrollDirection.UP:
132             y = value + delta
133         elif event.direction == Gdk.ScrollDirection.DOWN:
134             y = value - delta
135         elif event.direction == Gdk.ScrollDirection.SMOOTH:
136             y = value - event.delta_y * delta
137
138         if y >= 1:
139             y = 1
140         elif y <= 0:
141             y = 0
142         self.adjustment.set_value(y)
143         return True
144
145 class CustomSliderWidget(Gtk.DrawingArea):
146     def __init__(self, adjustment):
147         Gtk.DrawingArea.__init__(self)
148
149         self.adjustment = adjustment
150
151         self.connect("draw", self.on_expose)
152         self.connect("size_allocate", self.on_size_allocate)
153         adjustment.connect("value-changed", self.on_value_changed)
154         self.connect("button-press-event", self.on_mouse)
155         self.connect("motion-notify-event", self.on_mouse)
156         self.connect("scroll-event", self.on_scroll)
157         self.set_events(Gdk.EventMask.BUTTON1_MOTION_MASK |
158                 Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.BUTTON_PRESS_MASK)
159
160     def on_scroll(self, widget, event):
161         delta = self.adjustment.step_increment
162         value = self.adjustment.get_value()
163         if event.direction == Gdk.ScrollDirection.UP:
164             y = value + delta
165         elif event.direction == Gdk.ScrollDirection.DOWN:
166             y = value - delta
167         if y >= 1:
168             y = 1
169         elif y <= 0:
170             y = 0
171         self.adjustment.set_value(y)
172         return True
173
174     def on_mouse(self, widget, event):
175         if event.type == Gdk.EventType.BUTTON_PRESS:
176             #print "mouse button %u pressed %u:%u" % (event.button, event.x, event.y)
177             if event.button == 1:
178                 if event.y >= self.slider_rail_up and event.y < self.slider_rail_up + self.slider_rail_height:
179                     self.adjustment.set_value(1 - float(event.y - self.slider_rail_up)/float(self.slider_rail_height))
180         elif event.type == Gdk.EventType.MOTION_NOTIFY:
181             #print "mouse motion %u:%u" % (event.x, event.y)
182             if event.y < self.slider_rail_up:
183                 y = self.slider_rail_up
184             elif event.y > self.slider_rail_up + self.slider_rail_height:
185                 y = self.slider_rail_up + self.slider_rail_height
186             else:
187                 y = event.y
188             self.adjustment.set_value(1 - float(y - self.slider_rail_up)/float(self.slider_rail_height))
189
190         return False
191
192     def on_value_changed(self, adjustment):
193         self.invalidate_all()
194
195     def on_expose(self, widget, cairo_ctx):
196
197         self.draw(cairo_ctx)
198
199         return False
200
201     def get_preferred_width(self, widget):
202         minimal_width = natural_width = self.width
203         return (minimal_width, natural_width)
204
205     def get_preferred_height(self, widget):
206         requisition = Gtk.Requisition()
207         on_size_request(self, widget, requisition)
208         minimal_height = natural_heigt = requisition.height
209         return (minimal_height, natural_height)
210
211
212     def on_size_allocate(self, widget, allocation):
213         self.width = float(allocation.width)
214         self.height = float(allocation.height)
215         self.font_size = 10
216
217     def on_size_request(self, widget, requisition):
218         requisition.width = 20
219         return
220
221     def invalidate_all(self):
222         if hasattr(self, 'width') and hasattr(self, 'height'):
223             self.queue_draw_area(0, 0, int(self.width), int(self.height))
224
225     def draw(self, cairo_ctx):
226         if self.has_focus():
227             state = Gtk.StateType.PRELIGHT
228         else:
229             state = Gtk.StateType.NORMAL
230
231         #cairo_ctx.rectangle(0, 0, self.width, self.height)
232         #cairo_ctx.set_source_color(self.style.bg[state])
233         #cairo_ctx.fill_preserve()
234         #Gdk.cairo_set_source_color(cairo_ctx,
235         #        self.get_style_context().get_color(state).to_color())
236         #cairo_ctx.stroke()
237
238         slider_knob_width = 37.5 if self.width * 3 / 4 > 37.5 else self.width * 3 / 4
239         slider_knob_height = slider_knob_width * 2
240         slider_knob_height -= slider_knob_height % 2
241         slider_knob_height += 1
242
243         slider_x = self.width/2
244
245         cairo_ctx.set_line_width(1)
246
247         # slider rail
248         Gdk.cairo_set_source_color(cairo_ctx,
249                 self.get_style_context().get_color(state).to_color())
250         self.slider_rail_up = slider_knob_height/2 + (self.width - slider_knob_width)/2
251         self.slider_rail_height = self.height - 2 * self.slider_rail_up
252         cairo_ctx.move_to(slider_x, self.slider_rail_up)
253         cairo_ctx.line_to(slider_x, self.slider_rail_height + self.slider_rail_up)
254         cairo_ctx.stroke()
255
256         # slider knob
257         slider_y = round(self.slider_rail_up + self.slider_rail_height * (1 - self.adjustment.get_value()))
258         lg = cairo.LinearGradient(slider_x -
259                 float(slider_knob_width)/2, slider_y - slider_knob_height/2,
260                 slider_x - float(slider_knob_width)/2, slider_y +
261                 slider_knob_height/2)
262         slider_alpha = 1.0
263         lg.add_color_stop_rgba(0, 0.55, 0.55, 0.55, slider_alpha)
264         lg.add_color_stop_rgba(0.1, 0.65, 0.65, 0.65, slider_alpha)
265         lg.add_color_stop_rgba(0.1, 0.75, 0.75, 0.75, slider_alpha)
266         lg.add_color_stop_rgba(0.125, 0.75, 0.75, 0.75, slider_alpha)
267         lg.add_color_stop_rgba(0.125, 0.15, 0.15, 0.15, slider_alpha)
268         lg.add_color_stop_rgba(0.475, 0.35, 0.35, 0.35, slider_alpha)
269         lg.add_color_stop_rgba(0.475, 0, 0, 0, slider_alpha)
270         lg.add_color_stop_rgba(0.525, 0, 0, 0, slider_alpha)
271         lg.add_color_stop_rgba(0.525, 0.35, 0.35, 0.35, slider_alpha)
272         lg.add_color_stop_rgba(0.875, 0.65, 0.65, 0.65, slider_alpha)
273         lg.add_color_stop_rgba(0.875, 0.75, 0.75, 0.75, slider_alpha)
274         lg.add_color_stop_rgba(0.900, 0.75, 0.75, 0.75, slider_alpha)
275         lg.add_color_stop_rgba(0.900, 0.15, 0.15, 0.15, slider_alpha)
276         lg.add_color_stop_rgba(1.000, 0.10, 0.10, 0.10, slider_alpha)
277         cairo_ctx.rectangle(slider_x - float(slider_knob_width)/2,
278                             slider_y - slider_knob_height/2,
279                             float(slider_knob_width),
280                             slider_knob_height)
281         Gdk.cairo_set_source_color(cairo_ctx,
282                 self.get_style_context().get_background_color(state).to_color())
283         cairo_ctx.fill_preserve()
284         cairo_ctx.set_source(lg)
285         cairo_ctx.fill()