update german translation
[ardour.git] / libs / gtkmm2ext / fader.cc
1 /*
2   Copyright (C) 2014 Waves Audio Ltd.
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   $Id: fastmeter.h 570 2006-06-07 21:21:21Z sampo $
19 */
20
21
22 #include <iostream>
23
24 #include "pbd/stacktrace.h"
25
26 #include "gtkmm2ext/fader.h"
27 #include "gtkmm2ext/keyboard.h"
28 #include "gtkmm2ext/rgb_macros.h"
29 #include "gtkmm2ext/utils.h"
30 #include "pbd/failed_constructor.h"
31 #include "pbd/file_utils.h"
32 #include "ardour/filesystem_paths.h"
33
34 using namespace Gtkmm2ext;
35 using namespace Gtk;
36 using namespace std;
37
38 #define CORNER_RADIUS 4
39 #define CORNER_SIZE   2
40 #define CORNER_OFFSET 1
41 #define FADER_RESERVE 5
42
43
44 static void get_closest_point_on_line(double xa, double ya, double xb, double yb, double xp, double yp, double& xl, double& yl )
45 {
46         // Storing vector A->B
47         double a_to_b_x = xb - xa;
48         double a_to_b_y = yb - ya;
49
50         // Storing vector A->P
51         double a_to_p_x = xp - xa;
52         double a_to_p_y = yp - ya;
53
54
55         // Basically finding the squared magnitude
56         // of a_to_b
57         double atb2 = a_to_b_x * a_to_b_x + a_to_b_y * a_to_b_y;
58
59         // The dot product of a_to_p and a_to_b
60         double atp_dot_atb = a_to_p_x * a_to_b_x + a_to_p_y * a_to_b_y;
61
62         // The normalized "distance" from a to
63         // your closest point
64         double t = atp_dot_atb / atb2;
65
66         // Add the distance to A, moving
67         // towards B
68         double x = xa + a_to_b_x * t;
69         double y = ya + a_to_b_y * t;
70
71         if ((xa != xb)) {
72                 if ((x < xa) && (x < xb)) {
73                         if (xa < xb) {
74                                 x = xa;
75                                 y = ya;
76                         } else {
77                                 x = xb;
78                                 y = yb;
79                         }
80                 } else if ((x > xa) && (x > xb)) {
81                         if (xb > xa) {
82                                 x = xb;
83                                 y = yb;
84                         } else {
85                                 x = xa;
86                                 y = ya;
87                         }
88                 }
89         } else {
90                 if ((y < ya) && (y < yb)) {
91                         if (ya < yb) {
92                                 x = xa;
93                                 y = ya;
94                         } else {
95                                 x = xb;
96                                 y = yb;
97                         }
98                 } else if ((y > ya) && (y > yb)) {
99                         if (yb > ya) {
100                                 x = xb;
101                                 y = yb;
102                         } else {
103                                 x = xa;
104                                 y = ya;
105                         }
106                 }
107         }
108
109         xl = x;
110         yl = y;
111 }
112
113 Fader::Fader (Gtk::Adjustment& adj,
114               const Glib::RefPtr<Gdk::Pixbuf>& face_pixbuf,
115               const Glib::RefPtr<Gdk::Pixbuf>& active_face_pixbuf,
116               const Glib::RefPtr<Gdk::Pixbuf>& underlay_pixbuf,
117               const Glib::RefPtr<Gdk::Pixbuf>& handle_pixbuf,
118               const Glib::RefPtr<Gdk::Pixbuf>& active_handle_pixbuf,
119               int min_pos_x,
120               int min_pos_y,
121               int max_pos_x,
122               int max_pos_y,
123               bool read_only)
124         : adjustment (adj)
125         , _face_pixbuf (face_pixbuf)
126         , _active_face_pixbuf (active_face_pixbuf)
127         , _underlay_pixbuf (underlay_pixbuf)
128         , _handle_pixbuf (handle_pixbuf)
129         , _active_handle_pixbuf (active_handle_pixbuf)
130         , _min_pos_x (min_pos_x)
131         , _min_pos_y (min_pos_y)
132         , _max_pos_x (max_pos_x)
133         , _max_pos_y (max_pos_y)
134         , _grab_window (0)
135         , _touch_cursor (0)
136         , _dragging (false)
137         , _default_value (adjustment.get_value())
138         , _read_only (read_only)
139 {
140         update_unity_position ();
141
142         if (!_read_only) {
143                 add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
144         }
145
146         adjustment.signal_value_changed().connect (mem_fun (*this, &Fader::adjustment_changed));
147         adjustment.signal_changed().connect (mem_fun (*this, &Fader::adjustment_changed));
148         CairoWidget::set_size_request(_face_pixbuf->get_width(), _face_pixbuf->get_height());
149 }
150
151 Fader::~Fader ()
152 {
153         if (_touch_cursor) {
154                 delete _touch_cursor;
155         }
156 }
157
158 void
159 Fader::get_image_scales (double &x_scale, double &y_scale)
160 {
161         int pbwidth = _face_pixbuf->get_width ();
162         int pbheight = _face_pixbuf->get_height ();
163         int width = get_width ();
164         int height = get_height ();
165
166         if ((width != pbwidth) || (height != pbheight)) {
167                 x_scale = double (width) / double (pbwidth);
168                 if (x_scale == 0.0) {
169                         x_scale = 1.0;
170                 }
171                 y_scale = double (height) / double (pbheight);
172                 if (y_scale == 0.0) {
173                         y_scale = 1.0;
174                 }
175         } else {
176                 x_scale = y_scale = 1.0;
177         }
178 }
179
180 void
181 Fader::set_touch_cursor (const Glib::RefPtr<Gdk::Pixbuf>& touch_cursor)
182 {
183         _touch_cursor = new Gdk::Cursor (Gdk::Display::get_default(), touch_cursor, 12, 12);
184 }
185
186 void
187 Fader::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle_t*)
188 {
189         cairo_t* cr = ctx->cobj();
190
191         double xscale = 1.0;
192         double yscale = 1.0;
193
194         get_image_scales (xscale, yscale);
195
196         cairo_matrix_t matrix;
197         cairo_get_matrix (cr, &matrix);
198         cairo_matrix_scale (&matrix, xscale, yscale);
199         cairo_set_matrix (cr, &matrix);
200
201         get_handle_position (_last_drawn_x, _last_drawn_y);
202
203         if (_underlay_pixbuf != 0) {
204                 gdk_cairo_set_source_pixbuf (cr,
205                                              _underlay_pixbuf->gobj(),
206                                              (_last_drawn_x - (int)((_underlay_pixbuf->get_width() * xscale) / 2.0 + 0.5)) / xscale,
207                                              (_last_drawn_y - (int)((_underlay_pixbuf->get_height() * yscale) / 2.0 + 0.5)) / yscale);
208                 cairo_paint (cr);
209         }
210
211         gdk_cairo_set_source_pixbuf (cr,
212                                      ((get_state () == Gtk::STATE_ACTIVE) && (_active_face_pixbuf != 0)) ?
213                                      _active_face_pixbuf->gobj() :
214                                      _face_pixbuf->gobj(),
215                                      0,
216                                      0);
217         cairo_paint (cr);
218
219         const Glib::RefPtr<Gdk::Pixbuf> handle_pixbuf (_dragging ? _active_handle_pixbuf : _handle_pixbuf);
220         gdk_cairo_set_source_pixbuf (cr,
221                                      handle_pixbuf->gobj(),
222                                      (_last_drawn_x - (int)((handle_pixbuf->get_width() * xscale) / 2.0 + 0.5)) / xscale,
223                                      (_last_drawn_y - (int)((handle_pixbuf->get_height() * yscale) / 2.0 + 0.5)) / yscale);
224         cairo_paint (cr);
225 }
226
227 void
228 Fader::on_size_request (GtkRequisition* req)
229 {
230         req->width = _face_pixbuf->get_width();
231         req->height = _face_pixbuf->get_height();
232 }
233
234 void
235 Fader::on_size_allocate (Gtk::Allocation& alloc)
236 {
237         CairoWidget::on_size_allocate(alloc);
238         update_unity_position ();
239 }
240
241 bool
242 Fader::on_button_press_event (GdkEventButton* ev)
243 {
244         focus_handler(this);
245
246         if (_read_only) {
247                 return false;
248         }
249
250         if (ev->type != GDK_BUTTON_PRESS) {
251                 return false;
252         }
253
254         if (ev->button != 1 && ev->button != 2) {
255                 return false;
256         }
257
258         if (_touch_cursor) {
259                 get_window()->set_cursor (*_touch_cursor);
260         }
261
262         _grab_start_mouse_x = ev->x;
263         _grab_start_mouse_y = ev->y;
264         get_handle_position (_grab_start_handle_x, _grab_start_handle_y);
265
266         double xscale = 1.0;
267         double yscale = 1.0;
268
269         get_image_scales (xscale, yscale);
270
271         double hw = _handle_pixbuf->get_width() * xscale;
272         double hh = _handle_pixbuf->get_height() * yscale;
273
274         if ((ev->x < (_grab_start_handle_x - hw/2)) || (ev->x > (_grab_start_handle_x + hw/2)) || (ev->y < (_grab_start_handle_y - hh/2)) || (ev->y > (_grab_start_handle_y + hh/2))) {
275                 return false;
276         }
277
278         double ev_pos_x;
279         double ev_pos_y;
280
281         get_closest_point_on_line(_min_pos_x, _min_pos_y,
282                                   _max_pos_x, _max_pos_y,
283                                   ev->x, ev->y,
284                                   ev_pos_x, ev_pos_y );
285         add_modal_grab ();
286
287         _grab_window = ev->window;
288         _dragging = true;
289
290         gdk_pointer_grab(ev->window,false,
291                          GdkEventMask (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK),
292                          NULL,
293                          NULL,
294                          ev->time);
295
296         queue_draw();
297
298         return true;
299 }
300
301 bool
302 Fader::on_button_release_event (GdkEventButton* ev)
303 {
304         if (_read_only) {
305                 return false;
306         }
307
308         if (_touch_cursor) {
309                 get_window()->set_cursor ();
310         }
311
312         if (_dragging) { //temp
313                 remove_modal_grab();
314                 _dragging = false;
315                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
316                 queue_draw ();
317         }
318         return false;
319 }
320
321 bool
322 Fader::on_scroll_event (GdkEventScroll* ev)
323 {
324         if (_read_only) {
325                 return false;
326         }
327
328         int step_factor = 1;
329
330         switch (ev->direction) {
331         case GDK_SCROLL_RIGHT:
332         case GDK_SCROLL_UP:
333 #ifdef __APPLE__
334                 if ( ev->state & GDK_SHIFT_MASK ) {
335                         step_factor = -1;
336                 } else {
337                         step_factor = 1;
338                 }
339 #else
340                 step_factor = 1;
341 #endif
342                 break;
343         case GDK_SCROLL_LEFT:
344         case GDK_SCROLL_DOWN:
345 #ifdef __APPLE__
346                 if ( ev->state & GDK_SHIFT_MASK ) {
347                         step_factor = 1;
348                 } else {
349                         step_factor = -1;
350                 }
351 #else
352                 step_factor = -1;
353 #endif
354                 break;
355         default:
356                 return false;
357         }
358         adjustment.set_value (adjustment.get_value() + step_factor * (adjustment.get_step_increment() ));
359         return true;
360 }
361
362 bool
363 Fader::on_motion_notify_event (GdkEventMotion* ev)
364 {
365         if (_read_only) {
366                 return false;
367         }
368
369         if (_dragging) {
370                 double ev_pos_x;
371                 double ev_pos_y;
372
373                 if (ev->window != _grab_window) {
374                         _grab_window = ev->window;
375                         return true;
376                 }
377
378                 get_closest_point_on_line(_min_pos_x, _min_pos_y,
379                                           _max_pos_x, _max_pos_y,
380                                           _grab_start_handle_x + (ev->x - _grab_start_mouse_x), _grab_start_handle_y + (ev->y - _grab_start_mouse_y),
381                                           ev_pos_x, ev_pos_y );
382
383                 double const fract = sqrt((ev_pos_x - _min_pos_x) * (ev_pos_x - _min_pos_x) +
384                                           (ev_pos_y - _min_pos_y) * (ev_pos_y - _min_pos_y)) /
385                         sqrt((double)((_max_pos_x - _min_pos_x) * (_max_pos_x - _min_pos_x) +
386                                       (_max_pos_y - _min_pos_y) * (_max_pos_y - _min_pos_y)));
387
388                 adjustment.set_value (adjustment.get_lower() + (adjustment.get_upper() - adjustment.get_lower()) * fract);
389         }
390         return true;
391 }
392
393 void
394 Fader::adjustment_changed ()
395 {
396         double handle_x;
397         double handle_y;
398         get_handle_position (handle_x, handle_y);
399
400         if ((handle_x != _last_drawn_x) || (handle_y != _last_drawn_y)) {
401                 queue_draw ();
402         }
403 }
404
405 /** @return pixel offset of the current value from the right or bottom of the fader */
406 void
407 Fader::get_handle_position (double& x, double& y)
408 {
409         double fract = (adjustment.get_value () - adjustment.get_lower()) / ((adjustment.get_upper() - adjustment.get_lower()));
410
411         x = (int)(_min_pos_x + (_max_pos_x - _min_pos_x) * fract);
412         y = (int)(_min_pos_y + (_max_pos_y - _min_pos_y) * fract);
413 }
414
415 bool
416 Fader::on_enter_notify_event (GdkEventCrossing*)
417 {
418         _hovering = true;
419         Keyboard::magic_widget_grab_focus ();
420         queue_draw ();
421         return false;
422 }
423
424 bool
425 Fader::on_leave_notify_event (GdkEventCrossing*)
426 {
427         if (_read_only) {
428                 return false;
429         }
430
431         if (!_dragging) {
432                 _hovering = false;
433                 Keyboard::magic_widget_drop_focus();
434                 queue_draw ();
435         }
436         return false;
437 }
438
439 void
440 Fader::set_default_value (float d)
441 {
442         _default_value = d;
443         update_unity_position ();
444 }
445
446 void
447 Fader::update_unity_position ()
448 {
449 }