don't show 'unity-line' (default value) for barcontrollers
[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.show_unity_line (false);
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
58         Gtk::SpinButton& spinner = _slider.get_spin_button();
59         spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
60         spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
61         spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
62         spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
63         spinner.set_digits (9);
64         spinner.set_numeric (true);
65         spinner.set_name ("BarControlSpinner");
66         add (_slider);
67         show_all ();
68 }
69
70 BarController::~BarController ()
71 {
72 }
73
74 bool
75 BarController::on_button_press_event (GdkEventButton* ev)
76 {
77         if (get_child() != &_slider) {
78                 return false;
79         }
80         if (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS) {
81                 _switch_on_release = true;
82                 return true;
83         } else {
84                 _switch_on_release = false;
85         }
86         return false;
87 }
88
89 bool
90 BarController::on_button_release_event (GdkEventButton* ev)
91 {
92         if (get_child() != &_slider) {
93                 return false;
94         }
95         if (ev->button == 1 && _switch_on_release) {
96                 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
97                 return true;
98         }
99         return false;
100 }
101
102 gint
103 BarController::switch_to_bar ()
104 {
105         if (_switching || get_child() == &_slider) {
106                 return FALSE;
107         }
108         _switching = true;
109         remove ();
110         add (_slider);
111         _slider.show ();
112         _slider.queue_draw ();
113         _switching = false;
114         SpinnerActive (false); /* EMIT SIGNAL */
115         return FALSE;
116 }
117
118 gint
119 BarController::switch_to_spinner ()
120 {
121         if (_switching || get_child() != &_slider) {
122                 return FALSE;
123         }
124
125         _switching = true;
126         Gtk::SpinButton& spinner = _slider.get_spin_button();
127         if (spinner.get_parent()) {
128                 spinner.get_parent()->remove(spinner);
129         }
130         remove ();
131         add (spinner);
132         spinner.show ();
133         spinner.select_region (0, spinner.get_text_length());
134         spinner.grab_focus ();
135         _switching = false;
136         SpinnerActive (true); /* EMIT SIGNAL */
137         return FALSE;
138 }
139
140 void
141 BarController::entry_activated ()
142 {
143         switch_to_bar ();
144 }
145
146 bool
147 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
148 {
149         entry_activated ();
150         return true;
151 }
152
153 void
154 BarController::before_expose ()
155 {
156         double xpos = -1;
157         _slider.set_text (get_label (xpos), false, false);
158 }
159
160 void
161 BarController::set_sensitive (bool yn)
162 {
163         Alignment::set_sensitive (yn);
164         _slider.set_sensitive (yn);
165 }
166
167 /* 
168     This is called when we need to update the adjustment with the value
169     from the spinner's text entry.
170     
171     We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
172     
173     If we're not in logarithmic mode we can return false to use the 
174     default conversion.
175     
176     In theory we should check for conversion errors but set numeric
177     mode to true on the spinner prevents invalid input.
178 */
179 int
180 BarController::entry_input (double* new_value)
181 {
182         if (!_logarithmic) {
183                 return false;
184         }
185
186         // extract a double from the string and take its log
187         Gtk::SpinButton& spinner = _slider.get_spin_button();
188         Entry *entry = dynamic_cast<Entry *>(&spinner);
189         double value;
190
191         {
192                 // Switch to user's preferred locale so that
193                 // if they use different LC_NUMERIC conventions,
194                 // we will honor them.
195
196                 PBD::LocaleGuard lg ("");
197                 sscanf (entry->get_text().c_str(), "%lf", &value);
198         }
199
200         *new_value = log(value);
201
202         return true;
203 }
204
205 /* 
206     This is called when we need to update the spinner's text entry 
207     with the value of the adjustment.
208     
209     We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
210     
211     If we're not in logarithmic mode we can return false to use the 
212     default conversion.
213 */
214 bool
215 BarController::entry_output ()
216 {
217         if (!_logarithmic) {
218                 return false;
219         }
220
221         char buf[128];
222         Gtk::SpinButton& spinner = _slider.get_spin_button();
223
224         // generate the exponential and turn it into a string
225         // convert to correct locale. 
226         
227         stringstream stream;
228         string str;
229
230         {
231                 // Switch to user's preferred locale so that
232                 // if they use different LC_NUMERIC conventions,
233                 // we will honor them.
234                 
235                 PBD::LocaleGuard lg ("");
236                 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
237         }
238
239         Entry *entry = dynamic_cast<Entry *>(&spinner);
240         entry->set_text(buf);
241         
242         return true;
243 }