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.
21 from gi
.repository
import Gtk
22 from gi
.repository
import Gdk
23 from gi
.repository
import GObject
26 log
= logging
.getLogger(__name__
)
29 class AdjustmentdBFS(Gtk
.Adjustment
):
30 def __init__(self
, scale
, default_db
, step_inc
):
31 self
.default_value
= scale
.db_to_scale(default_db
)
34 self
.step_increment
= step_inc
35 super().__init
__(self
.default_value
, 0.0, 1.0, step_inc
)
36 self
.connect("value-changed", self
.on_value_changed
)
37 self
.disable_value_notify
= False
40 self
.set_value(self
.get_value() + self
.step_increment
)
43 self
.set_value(self
.get_value() - self
.step_increment
)
46 self
.set_value(self
.default_value
)
48 def get_value_db(self
):
51 def set_value_db(self
, db
, from_midi
=False):
53 self
.disable_value_notify
= True
54 self
.set_value(self
.scale
.db_to_scale(db
))
55 self
.disable_value_notify
= False
57 self
.emit("volume-changed-from-midi")
59 self
.emit("volume-changed")
61 def on_value_changed(self
, adjustment
):
62 if not self
.disable_value_notify
:
63 self
.db
= self
.scale
.scale_to_db(self
.get_value())
64 self
.emit("volume-changed")
66 def set_scale(self
, scale
):
68 self
.disable_value_notify
= True
69 self
.set_value(self
.scale
.db_to_scale(self
.db
))
70 self
.disable_value_notify
= False
76 GObject
.SignalFlags
.RUN_FIRST | GObject
.SignalFlags
.ACTION
,
82 "volume-changed-from-midi",
84 GObject
.SignalFlags
.RUN_FIRST | GObject
.SignalFlags
.ACTION
,
90 class BalanceAdjustment(Gtk
.Adjustment
):
92 super().__init
__(0.0, -1.0, 1.0, 0.1)
93 self
.connect("value-changed", self
.on_value_changed
)
94 self
.disable_value_notify
= False
96 def set_balance(self
, value
, from_midi
=False):
97 self
.disable_value_notify
= True
99 self
.disable_value_notify
= False
101 self
.emit("balance-changed")
103 def on_value_changed(self
, adjustment
):
104 if not self
.disable_value_notify
:
105 self
.emit("balance-changed")
111 GObject
.SignalFlags
.RUN_FIRST | GObject
.SignalFlags
.ACTION
,
117 class VolumeSlider(Gtk
.Scale
):
118 def __init__(self
, adjustment
):
119 super().__init
__(orientation
=Gtk
.Orientation
.VERTICAL
)
120 self
.adjustment
= adjustment
121 self
.set_adjustment(adjustment
)
122 self
.set_draw_value(False)
123 self
.set_inverted(True)
124 self
._button
_down
= False
125 self
._button
_down
_y
= 0
126 self
._button
_down
_value
= 0
128 self
.connect("button-press-event", self
.button_press_event
)
129 self
.connect("button-release-event", self
.button_release_event
)
130 self
.connect("motion-notify-event", self
.motion_notify_event
)
131 self
.connect("scroll-event", self
.scroll_event
)
133 def button_press_event(self
, widget
, event
):
134 if event
.button
== 1:
135 if event
.state
& Gdk
.ModifierType
.CONTROL_MASK
:
136 if event
.type == Gdk
.EventType
.BUTTON_PRESS
:
137 self
.adjustment
.set_value_db(0)
139 elif event
.type == Gdk
.EventType
.BUTTON_PRESS
:
140 self
._button
_down
= True
141 self
._button
_down
_y
= event
.y
142 self
._button
_down
_value
= self
.adjustment
.get_value()
144 elif event
.type == Gdk
.EventType
._2BUTTON
_PRESS
:
145 self
.adjustment
.set_value(0)
150 def button_release_event(self
, widget
, event
):
151 self
._button
_down
= False
154 def motion_notify_event(self
, widget
, event
):
155 slider_length
= widget
.get_allocation().height
- widget
.get_style_context().get_property(
156 "min-height", Gtk
.StateFlags
.NORMAL
158 if self
._button
_down
:
159 delta_y
= (self
._button
_down
_y
- event
.y
) / slider_length
160 y
= self
._button
_down
_value
+ delta_y
166 self
.adjustment
.set_value(y
)
169 def scroll_event(self
, widget
, event
):
170 delta
= self
.adjustment
.step_increment
171 value
= self
.adjustment
.get_value()
172 if event
.direction
== Gdk
.ScrollDirection
.UP
:
174 elif event
.direction
== Gdk
.ScrollDirection
.DOWN
:
176 elif event
.direction
== Gdk
.ScrollDirection
.SMOOTH
:
177 y
= value
- event
.delta_y
* delta
184 self
.adjustment
.set_value(y
)
188 class BalanceSlider(Gtk
.Scale
):
189 def __init__(self
, adjustment
, preferred_width
, preferred_height
):
190 super().__init
__(orientation
=Gtk
.Orientation
.HORIZONTAL
)
191 self
.adjustment
= adjustment
192 self
.set_adjustment(adjustment
)
193 self
.set_has_origin(False)
194 self
.set_draw_value(False)
195 self
.set_property("has-tooltip", True)
196 self
._preferred
_width
= preferred_width
197 self
._preferred
_height
= preferred_height
198 self
._button
_down
= False
200 self
.add_mark(-1.0, Gtk
.PositionType
.TOP
)
201 self
.add_mark(0.0, Gtk
.PositionType
.TOP
)
202 self
.add_mark(1.0, Gtk
.PositionType
.TOP
)
204 self
.connect("button-press-event", self
.on_button_press_event
)
205 self
.connect("button-release-event", self
.on_button_release_event
)
206 self
.connect("motion-notify-event", self
.on_motion_notify_event
)
207 self
.connect("scroll-event", self
.on_scroll_event
)
208 self
.connect("query-tooltip", self
.on_query_tooltip
)
210 def get_preferred_width(self
):
211 return self
._preferred
_width
213 def get_preferred_height(self
):
214 return self
._preferred
_height
216 def on_button_press_event(self
, widget
, event
):
217 if event
.button
== 1:
218 if event
.type == Gdk
.EventType
.BUTTON_PRESS
:
219 self
._button
_down
= True
220 self
._button
_down
_x
= event
.x
221 self
._button
_down
_value
= self
.get_value()
223 elif event
.type == Gdk
.EventType
._2BUTTON
_PRESS
:
224 self
.adjustment
.set_balance(0)
229 def on_button_release_event(self
, widget
, event
):
230 self
._button
_down
= False
233 def on_motion_notify_event(self
, widget
, event
):
234 slider_length
= widget
.get_allocation().width
- widget
.get_style_context().get_property(
235 "min-width", Gtk
.StateFlags
.NORMAL
238 if self
._button
_down
:
239 delta_x
= (event
.x
- self
._button
_down
_x
) / slider_length
240 x
= self
._button
_down
_value
+ 2 * delta_x
241 self
.adjustment
.set_balance(min(1, max(x
, -1)))
246 def on_query_tooltip(self
, widget
, x
, y
, keyboard_mode
, tooltip
, *args
):
247 val
= int(self
.adjustment
.get_value() * 50)
249 tooltip
.set_text("Center")
251 tooltip
.set_text("Left: %s / Right: %d" % (50 - val
, val
+ 50))
255 def on_scroll_event(self
, widget
, event
):
256 delta
= self
.get_adjustment().get_step_increment()
257 value
= self
.get_value()
259 if event
.direction
== Gdk
.ScrollDirection
.UP
:
261 elif event
.direction
== Gdk
.ScrollDirection
.DOWN
:
263 elif event
.direction
== Gdk
.ScrollDirection
.SMOOTH
:
264 x
= value
- event
.delta_y
* delta
266 self
.set_value(min(1, max(x
, -1)))
270 class CustomSliderWidget(Gtk
.DrawingArea
):
271 def __init__(self
, adjustment
):
273 self
.adjustment
= adjustment
275 self
.connect("draw", self
.on_expose
)
276 self
.connect("size_allocate", self
.on_size_allocate
)
277 adjustment
.connect("value-changed", self
.on_value_changed
)
278 self
.connect("button-press-event", self
.on_mouse
)
279 self
.connect("motion-notify-event", self
.on_mouse
)
280 self
.connect("scroll-event", self
.on_scroll
)
282 Gdk
.EventMask
.BUTTON1_MOTION_MASK
283 | Gdk
.EventMask
.SCROLL_MASK
284 | Gdk
.EventMask
.BUTTON_PRESS_MASK
287 def on_scroll(self
, widget
, event
):
288 delta
= self
.adjustment
.step_increment
289 value
= self
.adjustment
.get_value()
290 if event
.direction
== Gdk
.ScrollDirection
.UP
:
292 elif event
.direction
== Gdk
.ScrollDirection
.DOWN
:
298 self
.adjustment
.set_value(y
)
301 def on_mouse(self
, widget
, event
):
302 if event
.type == Gdk
.EventType
.BUTTON_PRESS
:
303 log
.debug("Mouse button %u pressed %ux%u", event
.button
, event
.x
, event
.y
)
304 if event
.button
== 1:
306 event
.y
>= self
.slider_rail_up
307 and event
.y
< self
.slider_rail_up
+ self
.slider_rail_height
309 self
.adjustment
.set_value(
310 1 - float(event
.y
- self
.slider_rail_up
) / float(self
.slider_rail_height
)
312 elif event
.type == Gdk
.EventType
.MOTION_NOTIFY
:
313 log
.debug("Mouse motion %ux%u", event
.x
, event
.y
)
314 if event
.y
< self
.slider_rail_up
:
315 y
= self
.slider_rail_up
316 elif event
.y
> self
.slider_rail_up
+ self
.slider_rail_height
:
317 y
= self
.slider_rail_up
+ self
.slider_rail_height
320 self
.adjustment
.set_value(
321 1 - float(y
- self
.slider_rail_up
) / float(self
.slider_rail_height
)
326 def on_value_changed(self
, adjustment
):
327 self
.invalidate_all()
329 def on_expose(self
, widget
, cairo_ctx
):
333 def get_preferred_width(self
, widget
):
334 minimal_width
= natural_width
= self
.width
335 return (minimal_width
, natural_width
)
337 def get_preferred_height(self
, widget
):
338 requisition
= Gtk
.Requisition()
339 self
.on_size_request(self
, widget
, requisition
)
340 minimal_height
= natural_height
= requisition
.height
341 return (minimal_height
, natural_height
)
343 def on_size_allocate(self
, widget
, allocation
):
344 self
.width
= float(allocation
.width
)
345 self
.height
= float(allocation
.height
)
348 def on_size_request(self
, widget
, requisition
):
349 requisition
.width
= 20
351 def invalidate_all(self
):
352 if hasattr(self
, "width") and hasattr(self
, "height"):
353 self
.queue_draw_area(0, 0, int(self
.width
), int(self
.height
))
355 def draw(self
, cairo_ctx
):
357 state
= Gtk
.StateType
.PRELIGHT
359 state
= Gtk
.StateType
.NORMAL
361 # cairo_ctx.rectangle(0, 0, self.width, self.height)
362 # cairo_ctx.set_source_color(self.style.bg[state])
363 # cairo_ctx.fill_preserve()
364 # Gdk.cairo_set_source_color(cairo_ctx,
365 # self.get_style_context().get_color(state).to_color())
368 slider_knob_width
= 37.5 if self
.width
* 3 / 4 > 37.5 else self
.width
* 3 / 4
369 slider_knob_height
= slider_knob_width
* 2
370 slider_knob_height
-= slider_knob_height
% 2
371 slider_knob_height
+= 1
373 slider_x
= self
.width
/ 2
375 cairo_ctx
.set_line_width(1)
378 Gdk
.cairo_set_source_color(cairo_ctx
, self
.get_style_context().get_color(state
).to_color())
379 self
.slider_rail_up
= slider_knob_height
/ 2 + (self
.width
- slider_knob_width
) / 2
380 self
.slider_rail_height
= self
.height
- 2 * self
.slider_rail_up
381 cairo_ctx
.move_to(slider_x
, self
.slider_rail_up
)
382 cairo_ctx
.line_to(slider_x
, self
.slider_rail_height
+ self
.slider_rail_up
)
387 self
.slider_rail_up
+ self
.slider_rail_height
* (1 - self
.adjustment
.get_value())
389 lg
= cairo
.LinearGradient(
390 slider_x
- float(slider_knob_width
) / 2,
391 slider_y
- slider_knob_height
/ 2,
392 slider_x
- float(slider_knob_width
) / 2,
393 slider_y
+ slider_knob_height
/ 2,
396 lg
.add_color_stop_rgba(0, 0.55, 0.55, 0.55, slider_alpha
)
397 lg
.add_color_stop_rgba(0.1, 0.65, 0.65, 0.65, slider_alpha
)
398 lg
.add_color_stop_rgba(0.1, 0.75, 0.75, 0.75, slider_alpha
)
399 lg
.add_color_stop_rgba(0.125, 0.75, 0.75, 0.75, slider_alpha
)
400 lg
.add_color_stop_rgba(0.125, 0.15, 0.15, 0.15, slider_alpha
)
401 lg
.add_color_stop_rgba(0.475, 0.35, 0.35, 0.35, slider_alpha
)
402 lg
.add_color_stop_rgba(0.475, 0, 0, 0, slider_alpha
)
403 lg
.add_color_stop_rgba(0.525, 0, 0, 0, slider_alpha
)
404 lg
.add_color_stop_rgba(0.525, 0.35, 0.35, 0.35, slider_alpha
)
405 lg
.add_color_stop_rgba(0.875, 0.65, 0.65, 0.65, slider_alpha
)
406 lg
.add_color_stop_rgba(0.875, 0.75, 0.75, 0.75, slider_alpha
)
407 lg
.add_color_stop_rgba(0.900, 0.75, 0.75, 0.75, slider_alpha
)
408 lg
.add_color_stop_rgba(0.900, 0.15, 0.15, 0.15, slider_alpha
)
409 lg
.add_color_stop_rgba(1.000, 0.10, 0.10, 0.10, slider_alpha
)
411 slider_x
- float(slider_knob_width
) / 2,
412 slider_y
- slider_knob_height
/ 2,
413 float(slider_knob_width
),
416 Gdk
.cairo_set_source_color(
417 cairo_ctx
, self
.get_style_context().get_background_color(state
).to_color()
419 cairo_ctx
.fill_preserve()
420 cairo_ctx
.set_source(lg
)