f427b503897d930b20f82ed743e5cfbe573c27de
[ardour.git] / libs / gtkmm2ext / barcontroller.cc
1 /*
2     Copyright (C) 2004 Paul Davis
3     This program is free software; you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation; either version 2 of the License, or
6     (at your option) any later version.
7
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
18 #include <string>
19 #include <sstream>
20 #include <climits>
21 #include <cstdio>
22 #include <cmath>
23 #include <algorithm>
24
25 #include <pbd/controllable.h>
26 #include <pbd/locale_guard.h>
27
28 #include "gtkmm2ext/gtk_ui.h"
29 #include "gtkmm2ext/utils.h"
30 #include "gtkmm2ext/keyboard.h"
31 #include "gtkmm2ext/barcontroller.h"
32 #include "gtkmm2ext/cairo_widget.h"
33
34 #include "i18n.h"
35
36 using namespace std;
37 using namespace Gtk;
38 using namespace Gtkmm2ext;
39
40 BarController::BarController (Gtk::Adjustment& adj,
41                 boost::shared_ptr<PBD::Controllable> mc)
42         : _slider (&adj, 60, 16)
43         , _logarithmic (false)
44         , _switching (false)
45         , _switch_on_release (false)
46 {
47
48         add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
49         set (.5, .5, 1.0, 1.0);
50         set_border_width (0);
51         _slider.set_controllable (mc);
52         _slider.set_tweaks (PixFader::NoShowUnityLine);
53
54         _slider.StartGesture.connect (sigc::mem_fun(*this, &BarController::passtrhu_gesture_start));
55         _slider.StopGesture.connect (sigc::mem_fun(*this, &BarController::passtrhu_gesture_stop));
56         _slider.OnExpose.connect (sigc::mem_fun(*this, &BarController::before_expose));
57         _slider.set_name (get_name());
58
59         Gtk::SpinButton& spinner = _slider.get_spin_button();
60         spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
61         spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
62         spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
63         spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
64         spinner.set_digits (9);
65         spinner.set_numeric (true);
66         spinner.set_name ("BarControlSpinner");
67         add (_slider);
68         show_all ();
69 }
70
71 BarController::~BarController ()
72 {
73 }
74
75 bool
76 BarController::on_button_press_event (GdkEventButton* ev)
77 {
78         if (get_child() != &_slider) {
79                 return false;
80         }
81         if (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS) {
82                 _switch_on_release = true;
83                 return true;
84         } else {
85                 _switch_on_release = false;
86         }
87         return false;
88 }
89
90 bool
91 BarController::on_button_release_event (GdkEventButton* ev)
92 {
93         if (get_child() != &_slider) {
94                 return false;
95         }
96         if (ev->button == 1 && _switch_on_release) {
97                 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
98                 return true;
99         }
100         return false;
101 }
102
103 void
104 BarController::on_style_changed (const Glib::RefPtr<Gtk::Style>&)
105 {
106         _slider.set_name (get_name());
107 }
108
109 gint
110 BarController::switch_to_bar ()
111 {
112         if (_switching || get_child() == &_slider) {
113                 return FALSE;
114         }
115         _switching = true;
116         remove ();
117         add (_slider);
118         _slider.show ();
119         _slider.queue_draw ();
120         _switching = false;
121         SpinnerActive (false); /* EMIT SIGNAL */
122         return FALSE;
123 }
124
125 gint
126 BarController::switch_to_spinner ()
127 {
128         if (_switching || get_child() != &_slider) {
129                 return FALSE;
130         }
131
132         _switching = true;
133         Gtk::SpinButton& spinner = _slider.get_spin_button();
134         if (spinner.get_parent()) {
135                 spinner.get_parent()->remove(spinner);
136         }
137         remove ();
138         add (spinner);
139         spinner.show ();
140         spinner.select_region (0, spinner.get_text_length());
141         spinner.grab_focus ();
142         _switching = false;
143         SpinnerActive (true); /* EMIT SIGNAL */
144         return FALSE;
145 }
146
147 void
148 BarController::entry_activated ()
149 {
150         switch_to_bar ();
151 }
152
153 bool
154 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
155 {
156         entry_activated ();
157         return true;
158 }
159
160 void
161 BarController::before_expose ()
162 {
163         double xpos = -1;
164         _slider.set_text (get_label (xpos), false, false);
165 }
166
167 void
168 BarController::set_sensitive (bool yn)
169 {
170         Alignment::set_sensitive (yn);
171         _slider.set_sensitive (yn);
172 }
173
174 /* 
175     This is called when we need to update the adjustment with the value
176     from the spinner's text entry.
177     
178     We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
179     
180     If we're not in logarithmic mode we can return false to use the 
181     default conversion.
182     
183     In theory we should check for conversion errors but set numeric
184     mode to true on the spinner prevents invalid input.
185 */
186 int
187 BarController::entry_input (double* new_value)
188 {
189         if (!_logarithmic) {
190                 return false;
191         }
192
193         // extract a double from the string and take its log
194         Gtk::SpinButton& spinner = _slider.get_spin_button();
195         Entry *entry = dynamic_cast<Entry *>(&spinner);
196         double value;
197
198         {
199                 // Switch to user's preferred locale so that
200                 // if they use different LC_NUMERIC conventions,
201                 // we will honor them.
202
203                 PBD::LocaleGuard lg ("");
204                 sscanf (entry->get_text().c_str(), "%lf", &value);
205         }
206
207         *new_value = log(value);
208
209         return true;
210 }
211
212 /* 
213     This is called when we need to update the spinner's text entry 
214     with the value of the adjustment.
215     
216     We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
217     
218     If we're not in logarithmic mode we can return false to use the 
219     default conversion.
220 */
221 bool
222 BarController::entry_output ()
223 {
224         if (!_logarithmic) {
225                 return false;
226         }
227
228         char buf[128];
229         Gtk::SpinButton& spinner = _slider.get_spin_button();
230
231         // generate the exponential and turn it into a string
232         // convert to correct locale. 
233         
234         stringstream stream;
235         string str;
236
237         {
238                 // Switch to user's preferred locale so that
239                 // if they use different LC_NUMERIC conventions,
240                 // we will honor them.
241                 
242                 PBD::LocaleGuard lg ("");
243                 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
244         }
245
246         Entry *entry = dynamic_cast<Entry *>(&spinner);
247         entry->set_text(buf);
248         
249         return true;
250 }