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 MeterWidget(Gtk.DrawingArea):
30 def __init__(self, scale):
31 GObject.GObject.__init__(self)
35 self.connect("draw", self.draw)
36 self.connect("size-allocate", self.on_size_allocate)
38 self.color_bg = Gdk.Color(0,0,0)
39 self.color_value = None
40 self.color_mark = Gdk.Color(int(65535 * 0.2), int(65535 * 0.2), int(65535 * 0.2))
43 self.cache_surface = None
45 def get_preferred_width(self):
46 log.debug('get_preferred_width called')
49 def get_preferred_height(self):
52 def set_color(self, color):
53 self.color_value = color
54 self.cache_surface = None
57 def on_expose(self, widget, event):
58 cairo_ctx = widget.window.cairo_create()
60 # set a clip region for the expose event
61 cairo_ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
68 def on_size_allocate(self, widget, allocation):
69 self.width = float(allocation.width)
70 self.height = float(allocation.height)
72 self.cache_surface = None
74 def on_size_request(self, widget, requisition):
75 log.debug("size-request, %u x %u", requisition.width, requisition.height)
76 requisition.width = 20
79 def invalidate_all(self):
80 self.queue_draw_area(0, 0, int(self.width), int(self.height))
82 def draw_background(self, cairo_ctx):
83 if not self.cache_surface:
84 self.cache_surface = cairo.Surface.create_similar(
85 cairo_ctx.get_target(),
89 cache_cairo_ctx = cairo.Context(self.cache_surface)
91 cache_cairo_ctx.set_source_rgba(0, 0, 0, 0)
92 cache_cairo_ctx.rectangle(0, 0, self.width, self.height)
93 cache_cairo_ctx.fill()
95 cache_cairo_ctx.set_source_rgba(0.2, 0.2, 0.2, 1)
96 cache_cairo_ctx.select_font_face("Fixed")
97 cache_cairo_ctx.set_font_size(self.font_size)
98 glyph_width = self.font_size * 3 / 5 # avarage glyph ratio
99 for mark in self.scale.get_marks():
100 mark_position = int(self.height * (1 - mark.scale))
101 cache_cairo_ctx.move_to(0, mark_position)
102 cache_cairo_ctx.line_to(self.width, mark_position)
103 cache_cairo_ctx.stroke()
104 x_correction = self.width / 2 - glyph_width * len(mark.text) / 2
105 cache_cairo_ctx.move_to(x_correction, mark_position - 2)
106 cache_cairo_ctx.show_text(mark.text)
107 cairo_ctx.set_source_surface(self.cache_surface, 0, 0)
110 def draw_value(self, cairo_ctx, value, x, width):
111 if self.color_value is not None:
112 cairo_ctx.set_source_rgb(self.color_value.red/65535.,
113 self.color_value.green/65535., self.color_value.blue/65535.)
116 gradient = cairo.LinearGradient(1, 1, width-1, height-1)
117 if self.scale.scale_id == "K20":
118 gradient.add_color_stop_rgb(0, 1, 0, 0)
119 gradient.add_color_stop_rgb(0.38, 1, 1, 0)
120 gradient.add_color_stop_rgb(0.5, 0, 1, 0)
121 gradient.add_color_stop_rgb(1, 0, 0, 1)
122 elif self.scale.scale_id == "K14":
123 gradient.add_color_stop_rgb(0, 1, 0, 0)
124 gradient.add_color_stop_rgb(1-self.scale.db_to_scale(-14), 1, 1, 0)
125 gradient.add_color_stop_rgb(1-self.scale.db_to_scale(-24), 0, 1, 0)
126 gradient.add_color_stop_rgb(1, 0, 0, 1)
128 gradient.add_color_stop_rgb(0, 1, 0, 0)
129 gradient.add_color_stop_rgb(0.2, 1, 1, 0)
130 gradient.add_color_stop_rgb(1, 0, 1, 0)
131 cairo_ctx.set_source(gradient)
132 cairo_ctx.rectangle(x, self.height * (1 - value), width, self.height * value)
135 def draw_peak(self, cairo_ctx, value, x, width):
136 cairo_ctx.set_source_rgb(1, 1, 1)
137 cairo_ctx.rectangle(x, self.height * (1 - value), width, 2.5)
140 def set_scale(self, scale):
142 self.cache_surface = None
143 self.invalidate_all()
146 class MonoMeterWidget(MeterWidget):
147 def __init__(self, scale):
148 MeterWidget.__init__(self, scale)
154 def draw(self, widget, cairo_ctx):
155 self.draw_background(cairo_ctx)
156 self.draw_value(cairo_ctx, self.value, self.width/4.0, self.width/2.0)
157 self.draw_peak(cairo_ctx, self.pk, self.width/4.0, self.width/2.0)
159 def set_values(self, pk, value):
160 if value == self.raw_value and pk == self.raw_pk:
162 self.raw_value = value
164 old_value = self.value
166 self.value = self.scale.db_to_scale(value)
167 self.pk = self.scale.db_to_scale(pk)
168 if (abs(old_value-self.value) * self.height) > 0.01 or (abs(old_pk-self.pk) * self.height) > 0.01:
169 self.invalidate_all()
172 class StereoMeterWidget(MeterWidget):
173 def __init__(self, scale):
174 MeterWidget.__init__(self, scale)
181 self.raw_left_pk = 0.0
182 self.raw_right_pk = 0.0
187 def draw(self, widget, cairo_ctx):
188 self.draw_background(cairo_ctx)
189 self.draw_value(cairo_ctx, self.left, self.width/5.0, self.width/5.0)
190 self.draw_value(cairo_ctx, self.right, self.width/5.0 * 3.0, self.width/5.0)
191 self.draw_peak(cairo_ctx, self.pk_left, self.width/5.0, self.width/5.0)
192 self.draw_peak(cairo_ctx, self.pk_right, self.width/5.0 * 3.0, self.width/5.0)
195 def set_values(self, pk_l, pk_r, left, right):
196 if left == self.raw_left and right == self.raw_right and pk_l == self.raw_left_pk and pk_r == self.raw_right_pk:
199 self.raw_right = right
200 self.raw_left_pk = pk_l
201 self.raw_right_pk = pk_r
203 old_right = self.right
204 old_pk_left = self.pk_left
205 old_pk_right = self.pk_right
206 self.left = self.scale.db_to_scale(left)
207 self.right = self.scale.db_to_scale(right)
208 self.pk_left = self.scale.db_to_scale(pk_l)
209 self.pk_right = self.scale.db_to_scale(pk_r)
211 if (abs(old_left-self.left) * self.height) > 0.01 or (abs(old_right-self.right) * self.height) > 0.01 or (abs(old_pk_left-self.pk_left) * self.height) > 0.01 or (abs(old_pk_right-self.pk_right) * self.height) > 0.01:
212 self.invalidate_all()