2
Copyright (C) 2000-2007 Paul Davis
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
25
#include <gtkmm/window.h>
27
#include "pbd/controllable.h"
28
#include "pbd/compose.h"
30
#include "gtkmm2ext/gui_thread.h"
31
#include "gtkmm2ext/gtk_ui.h"
32
#include "gtkmm2ext/keyboard.h"
33
#include "gtkmm2ext/utils.h"
34
#include "gtkmm2ext/persistent_tooltip.h"
36
#include "ardour/pannable.h"
37
#include "ardour/panner.h"
39
#include "ardour_ui.h"
40
#include "global_signals.h"
41
#include "mono_panner.h"
42
#include "mono_panner_editor.h"
43
#include "rgb_macros.h"
50
using namespace Gtkmm2ext;
52
static const int pos_box_size = 9;
53
static const int lr_box_size = 15;
54
static const int step_down = 10;
55
static const int top_step = 2;
57
MonoPanner::ColorScheme MonoPanner::colors;
58
bool MonoPanner::have_colors = false;
60
MonoPanner::MonoPanner (boost::shared_ptr<ARDOUR::Panner> panner)
61
: PannerInterface (panner)
62
, position_control (_panner->pannable()->pan_azimuth_control)
65
, accumulated_delta (0)
67
, position_binder (position_control)
75
position_control->Changed.connect (connections, invalidator(*this), boost::bind (&MonoPanner::value_change, this), gui_context());
77
ColorsChanged.connect (sigc::mem_fun (*this, &MonoPanner::color_handler));
82
MonoPanner::~MonoPanner ()
88
MonoPanner::set_tooltip ()
90
double pos = position_control->get_value(); // 0..1
92
/* We show the position of the center of the image relative to the left & right.
93
This is expressed as a pair of percentage values that ranges from (100,0)
94
(hard left) through (50,50) (hard center) to (0,100) (hard right).
96
This is pretty wierd, but its the way audio engineers expect it. Just remember that
97
the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
101
snprintf (buf, sizeof (buf), _("L:%3d R:%3d"),
102
(int) rint (100.0 * (1.0 - pos)),
103
(int) rint (100.0 * pos));
104
_tooltip.set_tip (buf);
108
MonoPanner::on_expose_event (GdkEventExpose*)
110
Glib::RefPtr<Gdk::Window> win (get_window());
111
Glib::RefPtr<Gdk::GC> gc (get_style()->get_base_gc (get_state()));
112
Cairo::RefPtr<Cairo::Context> context = get_window()->create_cairo_context();
115
double pos = position_control->get_value (); /* 0..1 */
116
uint32_t o, f, t, b, pf, po;
117
const double corner_radius = 5;
120
height = get_height ();
125
b = colors.background;
126
pf = colors.pos_fill;
127
po = colors.pos_outline;
131
context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
132
context->rectangle (0, 0, width, height);
135
double usable_width = width - pos_box_size;
137
/* compute the centers of the L/R boxes based on the current stereo width */
139
if (fmod (usable_width,2.0) == 0) {
140
/* even width, but we need odd, so that there is an exact center.
141
So, offset cairo by 1, and reduce effective width by 1
144
context->translate (1.0, 0.0);
147
const double half_lr_box = lr_box_size/2.0;
151
left = 4 + half_lr_box; // center of left box
152
right = width - 4 - half_lr_box; // center of right box
155
context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
156
context->set_line_width (1.0);
157
context->move_to ((pos_box_size/2.0) + (usable_width/2.0), 0);
158
context->line_to ((pos_box_size/2.0) + (usable_width/2.0), height);
163
rounded_rectangle (context,
165
half_lr_box+step_down,
166
lr_box_size, lr_box_size, corner_radius);
167
context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
168
context->stroke_preserve ();
169
context->set_source_rgba (UINT_RGBA_R_FLT(f), UINT_RGBA_G_FLT(f), UINT_RGBA_B_FLT(f), UINT_RGBA_A_FLT(f));
175
left - half_lr_box + 3,
176
(lr_box_size/2) + step_down + 13);
177
context->select_font_face ("sans-serif", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD);
178
context->set_source_rgba (UINT_RGBA_R_FLT(t), UINT_RGBA_G_FLT(t), UINT_RGBA_B_FLT(t), UINT_RGBA_A_FLT(t));
179
context->show_text (_("L"));
183
rounded_rectangle (context,
185
half_lr_box+step_down,
186
lr_box_size, lr_box_size, corner_radius);
187
context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
188
context->stroke_preserve ();
189
context->set_source_rgba (UINT_RGBA_R_FLT(f), UINT_RGBA_G_FLT(f), UINT_RGBA_B_FLT(f), UINT_RGBA_A_FLT(f));
195
right - half_lr_box + 3,
196
(lr_box_size/2)+step_down + 13);
197
context->set_source_rgba (UINT_RGBA_R_FLT(t), UINT_RGBA_G_FLT(t), UINT_RGBA_B_FLT(t), UINT_RGBA_A_FLT(t));
198
context->show_text (_("R"));
200
/* 2 lines that connect them both */
201
context->set_source_rgba (UINT_RGBA_R_FLT(o), UINT_RGBA_G_FLT(o), UINT_RGBA_B_FLT(o), UINT_RGBA_A_FLT(o));
202
context->set_line_width (1.0);
204
/* make the lines a little longer than they need to be, because the corners of
205
the boxes are rounded and we don't want a gap
207
context->move_to (left + half_lr_box - corner_radius, half_lr_box+step_down);
208
context->line_to (right - half_lr_box + corner_radius, half_lr_box+step_down);
212
context->move_to (left + half_lr_box - corner_radius, half_lr_box+step_down+lr_box_size);
213
context->line_to (right - half_lr_box + corner_radius, half_lr_box+step_down+lr_box_size);
216
/* draw the position indicator */
218
double spos = (pos_box_size/2.0) + (usable_width * pos);
220
context->set_line_width (2.0);
221
context->move_to (spos + (pos_box_size/2.0), top_step); /* top right */
222
context->rel_line_to (0.0, pos_box_size); /* lower right */
223
context->rel_line_to (-pos_box_size/2.0, 4.0); /* bottom point */
224
context->rel_line_to (-pos_box_size/2.0, -4.0); /* lower left */
225
context->rel_line_to (0.0, -pos_box_size); /* upper left */
226
context->close_path ();
229
context->set_source_rgba (UINT_RGBA_R_FLT(po), UINT_RGBA_G_FLT(po), UINT_RGBA_B_FLT(po), UINT_RGBA_A_FLT(po));
230
context->stroke_preserve ();
231
context->set_source_rgba (UINT_RGBA_R_FLT(pf), UINT_RGBA_G_FLT(pf), UINT_RGBA_B_FLT(pf), UINT_RGBA_A_FLT(pf));
236
context->set_line_width (1.0);
237
context->move_to (spos, pos_box_size+4);
238
context->rel_line_to (0, half_lr_box+step_down);
239
context->set_source_rgba (UINT_RGBA_R_FLT(po), UINT_RGBA_G_FLT(po), UINT_RGBA_B_FLT(po), UINT_RGBA_A_FLT(po));
248
MonoPanner::on_button_press_event (GdkEventButton* ev)
250
if (PannerInterface::on_button_press_event (ev)) {
254
drag_start_x = ev->x;
258
_tooltip.target_stop_drag ();
259
accumulated_delta = 0;
262
/* Let the binding proxies get first crack at the press event
266
if (position_binder.button_press_handler (ev)) {
271
if (ev->button != 1) {
275
if (ev->type == GDK_2BUTTON_PRESS) {
276
int width = get_width();
278
if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
279
/* handled by button release */
284
if (ev->x <= width/3) {
285
/* left side dbl click */
286
position_control->set_value (0);
287
} else if (ev->x > 2*width/3) {
288
position_control->set_value (1.0);
290
position_control->set_value (0.5);
294
_tooltip.target_stop_drag ();
296
} else if (ev->type == GDK_BUTTON_PRESS) {
298
if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
299
/* handled by button release */
304
_tooltip.target_start_drag ();
312
MonoPanner::on_button_release_event (GdkEventButton* ev)
314
if (PannerInterface::on_button_release_event (ev)) {
318
if (ev->button != 1) {
323
_tooltip.target_stop_drag ();
324
accumulated_delta = 0;
327
if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
337
MonoPanner::on_scroll_event (GdkEventScroll* ev)
339
double one_degree = 1.0/180.0; // one degree as a number from 0..1, since 180 degrees is the full L/R axis
340
double pv = position_control->get_value(); // 0..1.0 ; 0 = left
343
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
346
step = one_degree * 5.0;
349
switch (ev->direction) {
351
case GDK_SCROLL_LEFT:
353
position_control->set_value (pv);
355
case GDK_SCROLL_DOWN:
356
case GDK_SCROLL_RIGHT:
358
position_control->set_value (pv);
366
MonoPanner::on_motion_notify_event (GdkEventMotion* ev)
373
double delta = (ev->x - last_drag_x) / (double) w;
375
/* create a detent close to the center */
377
if (!detented && ARDOUR::Panner::equivalent (position_control->get_value(), 0.5)) {
380
position_control->set_value (0.5);
384
accumulated_delta += delta;
386
/* have we pulled far enough to escape ? */
388
if (fabs (accumulated_delta) >= 0.025) {
389
position_control->set_value (position_control->get_value() + accumulated_delta);
391
accumulated_delta = false;
394
double pv = position_control->get_value(); // 0..1.0 ; 0 = left
395
position_control->set_value (pv + delta);
403
MonoPanner::on_key_press_event (GdkEventKey* ev)
405
double one_degree = 1.0/180.0;
406
double pv = position_control->get_value(); // 0..1.0 ; 0 = left
409
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
412
step = one_degree * 5.0;
415
switch (ev->keyval) {
418
position_control->set_value (pv);
422
position_control->set_value (pv);
426
position_control->set_value (0.0);
436
MonoPanner::set_colors ()
438
colors.fill = ARDOUR_UI::config()->canvasvar_MonoPannerFill.get();
439
colors.outline = ARDOUR_UI::config()->canvasvar_MonoPannerOutline.get();
440
colors.text = ARDOUR_UI::config()->canvasvar_MonoPannerText.get();
441
colors.background = ARDOUR_UI::config()->canvasvar_MonoPannerBackground.get();
442
colors.pos_outline = ARDOUR_UI::config()->canvasvar_MonoPannerPositionOutline.get();
443
colors.pos_fill = ARDOUR_UI::config()->canvasvar_MonoPannerPositionFill.get();
447
MonoPanner::color_handler ()
454
MonoPanner::editor ()
456
return new MonoPannerEditor (this);