~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to gtk2_ardour/mono_panner.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 2000-2007 Paul Davis
 
3
 
 
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.
 
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., 675 Mass Ave, Cambridge, MA 02139, USA.
 
17
 
 
18
*/
 
19
 
 
20
#include <iostream>
 
21
#include <iomanip>
 
22
#include <cstring>
 
23
#include <cmath>
 
24
 
 
25
#include <gtkmm/window.h>
 
26
 
 
27
#include "pbd/controllable.h"
 
28
#include "pbd/compose.h"
 
29
 
 
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"
 
35
 
 
36
#include "ardour/pannable.h"
 
37
#include "ardour/panner.h"
 
38
 
 
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"
 
44
#include "utils.h"
 
45
 
 
46
#include "i18n.h"
 
47
 
 
48
using namespace std;
 
49
using namespace Gtk;
 
50
using namespace Gtkmm2ext;
 
51
 
 
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;
 
56
 
 
57
MonoPanner::ColorScheme MonoPanner::colors;
 
58
bool MonoPanner::have_colors = false;
 
59
 
 
60
MonoPanner::MonoPanner (boost::shared_ptr<ARDOUR::Panner> panner)
 
61
        : PannerInterface (panner)
 
62
        , position_control (_panner->pannable()->pan_azimuth_control)
 
63
        , drag_start_x (0)
 
64
        , last_drag_x (0)
 
65
        , accumulated_delta (0)
 
66
        , detented (false)
 
67
        , position_binder (position_control)
 
68
        , _dragging (false)
 
69
{
 
70
        if (!have_colors) {
 
71
                set_colors ();
 
72
                have_colors = true;
 
73
        }
 
74
 
 
75
        position_control->Changed.connect (connections, invalidator(*this), boost::bind (&MonoPanner::value_change, this), gui_context());
 
76
 
 
77
        ColorsChanged.connect (sigc::mem_fun (*this, &MonoPanner::color_handler));
 
78
 
 
79
        set_tooltip ();
 
80
}
 
81
 
 
82
MonoPanner::~MonoPanner ()
 
83
{
 
84
        
 
85
}
 
86
 
 
87
void
 
88
MonoPanner::set_tooltip ()
 
89
{
 
90
        double pos = position_control->get_value(); // 0..1
 
91
 
 
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).
 
95
 
 
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.
 
98
        */
 
99
 
 
100
        char buf[64];
 
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);
 
105
}
 
106
 
 
107
bool
 
108
MonoPanner::on_expose_event (GdkEventExpose*)
 
109
{
 
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();
 
113
 
 
114
        int width, height;
 
115
        double pos = position_control->get_value (); /* 0..1 */
 
116
        uint32_t o, f, t, b, pf, po;
 
117
        const double corner_radius = 5;
 
118
 
 
119
        width = get_width();
 
120
        height = get_height ();
 
121
 
 
122
        o = colors.outline;
 
123
        f = colors.fill;
 
124
        t = colors.text;
 
125
        b = colors.background;
 
126
        pf = colors.pos_fill;
 
127
        po = colors.pos_outline;
 
128
 
 
129
        /* background */
 
130
 
 
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);
 
133
        context->fill ();
 
134
 
 
135
        double usable_width = width - pos_box_size;
 
136
 
 
137
        /* compute the centers of the L/R boxes based on the current stereo width */
 
138
 
 
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
 
142
                */
 
143
                usable_width -= 1.0;
 
144
                context->translate (1.0, 0.0);
 
145
        }
 
146
 
 
147
        const double half_lr_box = lr_box_size/2.0;
 
148
        double left;
 
149
        double right;
 
150
 
 
151
        left = 4 + half_lr_box; // center of left box
 
152
        right = width  - 4 - half_lr_box; // center of right box
 
153
 
 
154
        /* center line */
 
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);
 
159
        context->stroke ();
 
160
 
 
161
        /* left box */
 
162
 
 
163
        rounded_rectangle (context,
 
164
                          left - half_lr_box,
 
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));
 
170
        context->fill ();
 
171
 
 
172
        /* add text */
 
173
 
 
174
        context->move_to (
 
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"));
 
180
 
 
181
        /* right box */
 
182
 
 
183
        rounded_rectangle (context,
 
184
                           right - half_lr_box,
 
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));
 
190
        context->fill ();
 
191
 
 
192
        /* add text */
 
193
 
 
194
        context->move_to (
 
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"));
 
199
 
 
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);
 
203
 
 
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
 
206
        */
 
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);
 
209
        context->stroke ();
 
210
 
 
211
 
 
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);
 
214
        context->stroke ();
 
215
 
 
216
        /* draw the position indicator */
 
217
 
 
218
        double spos = (pos_box_size/2.0) + (usable_width * pos);
 
219
 
 
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 ();
 
227
 
 
228
 
 
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));
 
232
        context->fill ();
 
233
 
 
234
        /* marker line */
 
235
 
 
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));
 
240
        context->stroke ();
 
241
 
 
242
        /* done */
 
243
 
 
244
        return true;
 
245
}
 
246
 
 
247
bool
 
248
MonoPanner::on_button_press_event (GdkEventButton* ev)
 
249
{
 
250
        if (PannerInterface::on_button_press_event (ev)) {
 
251
                return true;
 
252
        }
 
253
        
 
254
        drag_start_x = ev->x;
 
255
        last_drag_x = ev->x;
 
256
 
 
257
        _dragging = false;
 
258
        _tooltip.target_stop_drag ();
 
259
        accumulated_delta = 0;
 
260
        detented = false;
 
261
 
 
262
        /* Let the binding proxies get first crack at the press event
 
263
         */
 
264
 
 
265
        if (ev->y < 20) {
 
266
                if (position_binder.button_press_handler (ev)) {
 
267
                        return true;
 
268
                }
 
269
        }
 
270
 
 
271
        if (ev->button != 1) {
 
272
                return false;
 
273
        }
 
274
 
 
275
        if (ev->type == GDK_2BUTTON_PRESS) {
 
276
                int width = get_width();
 
277
 
 
278
                if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
 
279
                        /* handled by button release */
 
280
                        return true;
 
281
                }
 
282
 
 
283
 
 
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);
 
289
                } else {
 
290
                        position_control->set_value (0.5);
 
291
                }
 
292
 
 
293
                _dragging = false;
 
294
                _tooltip.target_stop_drag ();
 
295
 
 
296
        } else if (ev->type == GDK_BUTTON_PRESS) {
 
297
 
 
298
                if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
 
299
                        /* handled by button release */
 
300
                        return true;
 
301
                }
 
302
 
 
303
                _dragging = true;
 
304
                _tooltip.target_start_drag ();
 
305
                StartGesture ();
 
306
        }
 
307
 
 
308
        return true;
 
309
}
 
310
 
 
311
bool
 
312
MonoPanner::on_button_release_event (GdkEventButton* ev)
 
313
{
 
314
        if (PannerInterface::on_button_release_event (ev)) {
 
315
                return true;
 
316
        }
 
317
 
 
318
        if (ev->button != 1) {
 
319
                return false;
 
320
        }
 
321
 
 
322
        _dragging = false;
 
323
        _tooltip.target_stop_drag ();
 
324
        accumulated_delta = 0;
 
325
        detented = false;
 
326
 
 
327
        if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
 
328
                _panner->reset ();
 
329
        } else {
 
330
                StopGesture ();
 
331
        }
 
332
 
 
333
        return true;
 
334
}
 
335
 
 
336
bool
 
337
MonoPanner::on_scroll_event (GdkEventScroll* ev)
 
338
{
 
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
 
341
        double step;
 
342
 
 
343
        if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
 
344
                step = one_degree;
 
345
        } else {
 
346
                step = one_degree * 5.0;
 
347
        }
 
348
 
 
349
        switch (ev->direction) {
 
350
        case GDK_SCROLL_UP:
 
351
        case GDK_SCROLL_LEFT:
 
352
                pv -= step;
 
353
                position_control->set_value (pv);
 
354
                break;
 
355
        case GDK_SCROLL_DOWN:
 
356
        case GDK_SCROLL_RIGHT:
 
357
                pv += step;
 
358
                position_control->set_value (pv);
 
359
                break;
 
360
        }
 
361
 
 
362
        return true;
 
363
}
 
364
 
 
365
bool
 
366
MonoPanner::on_motion_notify_event (GdkEventMotion* ev)
 
367
{
 
368
        if (!_dragging) {
 
369
                return false;
 
370
        }
 
371
 
 
372
        int w = get_width();
 
373
        double delta = (ev->x - last_drag_x) / (double) w;
 
374
 
 
375
        /* create a detent close to the center */
 
376
 
 
377
        if (!detented && ARDOUR::Panner::equivalent (position_control->get_value(), 0.5)) {
 
378
                detented = true;
 
379
                /* snap to center */
 
380
                position_control->set_value (0.5);
 
381
        }
 
382
 
 
383
        if (detented) {
 
384
                accumulated_delta += delta;
 
385
 
 
386
                /* have we pulled far enough to escape ? */
 
387
 
 
388
                if (fabs (accumulated_delta) >= 0.025) {
 
389
                        position_control->set_value (position_control->get_value() + accumulated_delta);
 
390
                        detented = false;
 
391
                        accumulated_delta = false;
 
392
                }
 
393
        } else {
 
394
                double pv = position_control->get_value(); // 0..1.0 ; 0 = left
 
395
                position_control->set_value (pv + delta);
 
396
        }
 
397
 
 
398
        last_drag_x = ev->x;
 
399
        return true;
 
400
}
 
401
 
 
402
bool
 
403
MonoPanner::on_key_press_event (GdkEventKey* ev)
 
404
{
 
405
        double one_degree = 1.0/180.0;
 
406
        double pv = position_control->get_value(); // 0..1.0 ; 0 = left
 
407
        double step;
 
408
 
 
409
        if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
 
410
                step = one_degree;
 
411
        } else {
 
412
                step = one_degree * 5.0;
 
413
        }
 
414
 
 
415
        switch (ev->keyval) {
 
416
        case GDK_Left:
 
417
                pv -= step;
 
418
                position_control->set_value (pv);
 
419
                break;
 
420
        case GDK_Right:
 
421
                pv += step;
 
422
                position_control->set_value (pv);
 
423
                break;
 
424
        case GDK_0:
 
425
        case GDK_KP_0:
 
426
                position_control->set_value (0.0);
 
427
                break;
 
428
        default:
 
429
                return false;
 
430
        }
 
431
 
 
432
        return true;
 
433
}
 
434
 
 
435
void
 
436
MonoPanner::set_colors ()
 
437
{
 
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();
 
444
}
 
445
 
 
446
void
 
447
MonoPanner::color_handler ()
 
448
{
 
449
        set_colors ();
 
450
        queue_draw ();
 
451
}
 
452
 
 
453
PannerEditor*
 
454
MonoPanner::editor ()
 
455
{
 
456
        return new MonoPannerEditor (this);
 
457
}