darken text color in stereo panner
[ardour.git] / gtk2_ardour / stereo_panner.cc
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
24 #include "pbd/controllable.h"
25 #include "pbd/compose.h"
26
27 #include "gtkmm2ext/gui_thread.h"
28 #include "gtkmm2ext/gtk_ui.h"
29
30 #include "ardour/panner.h"
31 #include "stereo_panner.h"
32
33 #include "i18n.h"
34
35 using namespace std;
36 using namespace Gtk;
37
38 static const int pos_box_size = 10;
39 static const int lr_box_size = 18;
40 static const int step_down = 10;
41
42 StereoPanner::StereoPanner (boost::shared_ptr<PBD::Controllable> position, boost::shared_ptr<PBD::Controllable> width)
43         : position_control (position)
44         , width_control (width)
45         , dragging (false)
46         , dragging_position (false)
47         , drag_start_x (0)
48         , last_drag_x (0)
49 {
50         position_control->Changed.connect (connections, invalidator(*this), boost::bind (&StereoPanner::value_change, this), gui_context());
51         width_control->Changed.connect (connections, invalidator(*this), boost::bind (&StereoPanner::value_change, this), gui_context());
52         set_tooltip ();
53
54         add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK|Gdk::POINTER_MOTION_MASK);
55 }
56
57 StereoPanner::~StereoPanner ()
58 {
59 }
60
61 void
62 StereoPanner::set_tooltip ()
63 {
64         Gtkmm2ext::UI::instance()->set_tip (this, string_compose (_("L:%1 R:%2 Width: %3%%"), 
65                                                                   (int) floor (position_control->get_value() * 100.0),
66                                                                   (int) floor ((1.0 - position_control->get_value() * 100)),
67                                                                   (int) floor (width_control->get_value() * 100.0)).c_str());
68 }
69
70 void
71 StereoPanner::value_change ()
72 {
73         set_tooltip ();
74         queue_draw ();
75 }
76
77 bool
78 StereoPanner::on_expose_event (GdkEventExpose* ev)
79 {
80         Glib::RefPtr<Gdk::Window> win (get_window());
81         Glib::RefPtr<Gdk::GC> gc (get_style()->get_base_gc (get_state()));
82
83         cairo_t* cr = gdk_cairo_create (win->gobj());
84        
85         int x1, x2;
86         int width, height;
87         double pos = position_control->get_value (); /* 0..1 */
88         double swidth = width_control->get_value (); /* -1..+1 */
89         const int border_width = 1;
90         const int border = border_width * 2;
91
92
93         width = get_width();
94         height = get_height ();
95
96         /* background */
97
98         cairo_set_source_rgb (cr, 0.184, 0.172, 0.172);
99         cairo_rectangle (cr, 0, 0, width, height);
100         cairo_fill (cr);
101
102         /* leave a border */
103
104         width -= border_width;
105         height -= border_width;
106
107         /* compute where the central box is */
108
109         x1 = (int) floor (width * pos);                      // center of widget 
110         x2 = x1 - (int) floor ((fabs (swidth) * width)/2.0); // center, then back up half the swidth value
111         x1 -= pos_box_size/2;                                // center, then back up by half width of position box
112
113         /* compute & draw the line through the box */
114         
115         cairo_set_line_width (cr, 2);
116         cairo_set_source_rgba (cr, 0.3137, 0.4431, 0.7843, 1.0);
117         cairo_move_to (cr, border + x2, 4+(pos_box_size/2)+step_down);
118         cairo_line_to (cr, border + x2, 4+(pos_box_size/2));
119         cairo_line_to (cr, border + x2 + floor ((fabs (swidth * width))), 4+(pos_box_size/2));
120         cairo_line_to (cr, border + x2 + floor ((fabs (swidth * width))), 4+(pos_box_size/2) + step_down);
121         cairo_stroke (cr);
122
123         /* left box */
124
125         cairo_rectangle (cr, 
126                          border+ x2 - lr_box_size/2, 
127                          (lr_box_size/2)+step_down, 
128                          lr_box_size, lr_box_size);
129         cairo_set_source_rgba (cr, 0.3137, 0.4431, 0.7843, 1.0);
130         cairo_stroke_preserve (cr);
131         cairo_set_source_rgba (cr, 0.4509, 0.7686, 0.8627, 0.8);
132         cairo_fill (cr);
133
134         
135         /* add text */
136
137         cairo_move_to (cr, 
138                        border + x2 - lr_box_size/2 + 4,
139                        (lr_box_size/2) + step_down + 13);
140         cairo_set_source_rgba (cr, 0.129, 0.054, 0.588, 1.0);
141         cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
142         cairo_show_text (cr, "L");
143
144         /* right box */
145
146         cairo_rectangle (cr, 
147                          border + x2 + (int) floor ((fabs (swidth * width))) - lr_box_size/2, 
148                          (lr_box_size/2)+step_down, 
149                          lr_box_size, lr_box_size);
150         cairo_set_source_rgba (cr, 0.3137, 0.4431, 0.7843, 1.0);
151         cairo_stroke_preserve (cr);
152         cairo_set_source_rgba (cr, 0.4509, 0.7686, 0.8627, 0.8);
153         cairo_fill (cr);
154
155         /* add text */
156
157         cairo_move_to (cr, 
158                        border + x2 + (int) floor ((fabs (swidth * width))) - lr_box_size/2 + 4, 
159                        (lr_box_size/2)+step_down + 13);
160         cairo_set_source_rgba (cr, 0.129, 0.054, 0.588, 1.0);
161         cairo_show_text (cr, "R");
162
163         /* draw the central box */
164
165         cairo_set_line_width (cr, 1);
166         cairo_rectangle (cr, border + x1, 4, pos_box_size, pos_box_size);
167         cairo_set_source_rgba (cr, 0.3137, 0.4431, 0.7843, 1.0);
168         cairo_stroke_preserve (cr);
169         cairo_set_source_rgba (cr, 0.4509, 0.7686, 0.8627, 0.8);
170         cairo_fill (cr);
171
172         /* done */
173
174         cairo_destroy (cr);
175         return true;
176 }
177
178 bool
179 StereoPanner::on_button_press_event (GdkEventButton* ev)
180 {
181         drag_start_x = ev->x;
182         last_drag_x = ev->x;
183
184         /* center 8 pixels are for position drag */
185
186         int w = get_width();
187         double pos = position_control->get_value ();
188
189         if ((ev->x >= (int) floor ((pos * w)-(pos_box_size/2))) && (ev->x <= (int) floor ((pos * w)+(pos_box_size/2)))) {
190                 dragging_position = true;
191         } else {
192                 dragging_position = false;
193         }
194
195         if (ev->type == GDK_2BUTTON_PRESS) {
196                 if (dragging_position) {
197                         cerr << "Reset pos\n";
198                         position_control->set_value (0.5); // reset position to center
199                 } else {
200                         cerr << "Reset width\n";
201                         width_control->set_value (1.0); // reset position to full, LR
202                 }
203                 dragging = false;
204         } else {
205                 dragging = true;
206         }
207
208         return true;
209 }
210
211 bool
212 StereoPanner::on_button_release_event (GdkEventButton* ev)
213 {
214         dragging = false;
215         dragging_position = false;
216         return true;
217 }
218
219 bool
220 StereoPanner::on_motion_notify_event (GdkEventMotion* ev)
221 {
222         if (!dragging) {
223                 return false;
224         }
225
226         int w = get_width();
227         float delta = (abs (ev->x - last_drag_x)) / (double) (w/2);
228
229         if (!dragging_position) {
230                 double wv = width_control->get_value();        
231                 
232                 if (((drag_start_x < w/2) && ev->x > last_drag_x) || // start left of center, move towards it
233                     ((drag_start_x > w/2) && ev->x < last_drag_x)) { // start right of center, move towards it
234                         wv = wv  * (1.0 - delta);
235                 } else {
236                         /* moving out, so increase the width */
237                         wv = wv * (1.0 + delta);
238                 }
239                 
240                 width_control->set_value (wv);
241
242         } else {
243
244                 double pv = position_control->get_value(); // 0..1.0 ; 0 = left
245                 
246                 if (ev->x > last_drag_x) { // increasing 
247                         pv = pv * (1.0 + delta);
248                 } else {
249                         pv = pv * (1.0 - delta);
250                 }
251
252                 position_control->set_value (pv);
253         }
254
255         last_drag_x = ev->x;
256         return true;
257 }