fix up file renaming code a little bit
[ArdourMidi.git] / libs / gtkmm2ext / barcontroller.cc
blob3e998bbcdbe6ec69df121db08cf52d1921f3e423
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.
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.
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.
17 $Id$
20 #include <string>
21 #include <sstream>
22 #include <climits>
23 #include <cstdio>
24 #include <cmath>
25 #include <algorithm>
27 #include <pbd/controllable.h>
29 #include "gtkmm2ext/gtk_ui.h"
30 #include "gtkmm2ext/utils.h"
31 #include "gtkmm2ext/keyboard.h"
32 #include "gtkmm2ext/barcontroller.h"
34 #include "i18n.h"
36 using namespace std;
37 using namespace Gtk;
38 using namespace Gtkmm2ext;
40 BarController::BarController (Gtk::Adjustment& adj,
41 boost::shared_ptr<PBD::Controllable> mc)
43 : adjustment (adj),
44 binding_proxy (mc),
45 spinner (adjustment)
48 _style = LeftToRight;
49 grabbed = false;
50 switching = false;
51 switch_on_release = false;
52 use_parent = false;
53 logarithmic = false;
55 layout = darea.create_pango_layout("");
57 set_shadow_type (SHADOW_NONE);
59 initial_value = adjustment.get_value ();
61 adjustment.signal_value_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
62 adjustment.signal_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
64 darea.add_events (Gdk::BUTTON_RELEASE_MASK|
65 Gdk::BUTTON_PRESS_MASK|
66 Gdk::POINTER_MOTION_MASK|
67 Gdk::ENTER_NOTIFY_MASK|
68 Gdk::LEAVE_NOTIFY_MASK|
69 Gdk::SCROLL_MASK);
71 darea.signal_expose_event().connect (mem_fun (*this, &BarController::expose));
72 darea.signal_motion_notify_event().connect (mem_fun (*this, &BarController::motion));
73 darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press), false);
74 darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release), false);
75 darea.signal_scroll_event().connect (mem_fun (*this, &BarController::scroll));
77 spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
78 spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
79 spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
80 spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
81 spinner.set_digits (3);
82 spinner.set_numeric (true);
84 add (darea);
85 show_all ();
88 void
89 BarController::drop_grab ()
91 if (grabbed) {
92 grabbed = false;
93 darea.remove_modal_grab();
94 StopGesture ();
98 bool
99 BarController::button_press (GdkEventButton* ev)
101 double fract;
103 if (binding_proxy.button_press_handler (ev)) {
104 return true;
107 switch (ev->button) {
108 case 1:
109 if (ev->type == GDK_2BUTTON_PRESS) {
110 switch_on_release = true;
111 drop_grab ();
112 } else {
113 switch_on_release = false;
114 darea.add_modal_grab();
115 grabbed = true;
116 grab_x = ev->x;
117 grab_window = ev->window;
118 StartGesture ();
120 return true;
121 break;
123 case 2:
124 fract = ev->x / (darea.get_width() - 2.0);
125 adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower()));
127 case 3:
128 break;
130 case 4:
131 case 5:
132 break;
135 return false;
138 bool
139 BarController::button_release (GdkEventButton* ev)
141 drop_grab ();
143 switch (ev->button) {
144 case 1:
145 if (switch_on_release) {
146 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
147 return true;
150 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
151 adjustment.set_value (initial_value);
152 } else {
153 double scale;
155 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
156 scale = 0.01;
157 } else if (ev->state & Keyboard::PrimaryModifier) {
158 scale = 0.1;
159 } else {
160 scale = 1.0;
163 mouse_control (ev->x, ev->window, scale);
165 break;
167 case 2:
168 break;
170 case 3:
171 return false;
173 default:
174 break;
177 return true;
180 bool
181 BarController::scroll (GdkEventScroll* ev)
183 double scale;
185 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
186 scale = 0.01;
187 } else if (ev->state & Keyboard::PrimaryModifier) {
188 scale = 0.1;
189 } else {
190 scale = 1.0;
193 switch (ev->direction) {
194 case GDK_SCROLL_UP:
195 case GDK_SCROLL_RIGHT:
196 adjustment.set_value (adjustment.get_value() + (scale * adjustment.get_step_increment()));
197 break;
199 case GDK_SCROLL_DOWN:
200 case GDK_SCROLL_LEFT:
201 adjustment.set_value (adjustment.get_value() - (scale * adjustment.get_step_increment()));
202 break;
205 return true;
208 bool
209 BarController::motion (GdkEventMotion* ev)
211 double scale;
213 if (!grabbed) {
214 return true;
217 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
218 return TRUE;
221 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
222 scale = 0.01;
223 } else if (ev->state & Keyboard::PrimaryModifier) {
224 scale = 0.1;
225 } else {
226 scale = 1.0;
229 return mouse_control (ev->x, ev->window, scale);
232 gint
233 BarController::mouse_control (double x, GdkWindow* window, double scaling)
235 double fract = 0.0;
236 double delta;
238 if (window != grab_window) {
239 grab_x = x;
240 grab_window = window;
241 return TRUE;
244 delta = x - grab_x;
245 grab_x = x;
247 switch (_style) {
248 case Line:
249 case LeftToRight:
250 fract = scaling * (delta / (darea.get_width() - 2));
251 fract = min (1.0, fract);
252 fract = max (-1.0, fract);
253 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
254 break;
256 default:
257 fract = 0.0;
261 return TRUE;
264 bool
265 BarController::expose (GdkEventExpose* /*event*/)
267 Glib::RefPtr<Gdk::Window> win (darea.get_window());
268 Widget* parent;
269 gint x1=0, x2=0, y1=0, y2=0;
270 gint w, h;
271 double fract;
273 fract = ((adjustment.get_value() - adjustment.get_lower()) /
274 (adjustment.get_upper() - adjustment.get_lower()));
276 switch (_style) {
277 case Line:
278 w = darea.get_width() - 1;
279 h = darea.get_height();
280 x1 = (gint) floor (w * fract);
281 x2 = x1;
282 y1 = 0;
283 y2 = h - 1;
285 if (use_parent) {
286 parent = get_parent();
288 if (parent) {
289 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
290 true,
291 0, 0, darea.get_width(), darea.get_height());
294 } else {
296 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
297 true,
298 0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
301 win->draw_line (get_style()->get_fg_gc (get_state()), x1, 0, x1, h);
302 break;
304 case CenterOut:
305 break;
307 case LeftToRight:
309 w = darea.get_width() - 2;
310 h = darea.get_height() - 2;
312 x1 = 0;
313 x2 = (gint) floor (w * fract);
314 y1 = 0;
315 y2 = h - 1;
317 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
318 false,
319 0, 0, darea.get_width() - 1, darea.get_height() - 1);
321 /* draw active box */
323 win->draw_rectangle (get_style()->get_fg_gc (get_state()),
324 true,
325 1 + x1,
326 1 + y1,
328 1 + y2);
330 /* draw inactive box */
332 win->draw_rectangle (get_style()->get_fg_gc (STATE_INSENSITIVE),
333 true,
334 1 + x2,
335 1 + y1,
336 w - x2,
337 1 + y2);
339 break;
341 case RightToLeft:
342 break;
343 case TopToBottom:
344 break;
345 case BottomToTop:
346 break;
349 /* draw label */
351 int xpos = -1;
352 std::string const label = get_label (xpos);
354 if (!label.empty()) {
356 layout->set_text (label);
358 int width, height;
359 layout->get_pixel_size (width, height);
361 if (xpos == -1) {
362 xpos = max (3, 1 + (x2 - (width/2)));
363 xpos = min (darea.get_width() - width - 3, xpos);
366 win->draw_layout (get_style()->get_text_gc (get_state()),
367 xpos,
368 (darea.get_height()/2) - (height/2),
369 layout);
372 return true;
375 void
376 BarController::set_style (barStyle s)
378 _style = s;
379 darea.queue_draw ();
382 gint
383 BarController::switch_to_bar ()
385 if (switching) {
386 return FALSE;
389 switching = true;
391 if (get_child() == &darea) {
392 return FALSE;
395 remove ();
396 add (darea);
397 darea.show ();
399 switching = false;
400 return FALSE;
403 gint
404 BarController::switch_to_spinner ()
406 if (switching) {
407 return FALSE;
410 switching = true;
412 if (get_child() == &spinner) {
413 return FALSE;
416 remove ();
417 add (spinner);
418 spinner.show ();
419 spinner.select_region (0, spinner.get_text_length());
420 spinner.grab_focus ();
422 switching = false;
423 return FALSE;
426 void
427 BarController::entry_activated ()
429 switch_to_bar ();
432 bool
433 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
435 entry_activated ();
436 return true;
439 void
440 BarController::set_use_parent (bool yn)
442 use_parent = yn;
443 queue_draw ();
446 void
447 BarController::set_sensitive (bool yn)
449 Frame::set_sensitive (yn);
450 darea.set_sensitive (yn);
454 This is called when we need to update the adjustment with the value
455 from the spinner's text entry.
457 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
459 If we're not in logarithmic mode we can return false to use the
460 default conversion.
462 In theory we should check for conversion errors but set numeric
463 mode to true on the spinner prevents invalid input.
466 BarController::entry_input (double* new_value)
468 if (!logarithmic) {
469 return false;
472 // extract a double from the string and take its log
473 Entry *entry = dynamic_cast<Entry *>(&spinner);
474 stringstream stream(entry->get_text());
475 stream.imbue(std::locale(""));
477 double value;
478 stream >> value;
480 *new_value = log(value);
481 return true;
485 This is called when we need to update the spinner's text entry
486 with the value of the adjustment.
488 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
490 If we're not in logarithmic mode we can return false to use the
491 default conversion.
493 bool
494 BarController::entry_output ()
496 if (!logarithmic) {
497 return false;
500 // generate the exponential and turn it into a string
501 // convert to correct locale.
503 stringstream stream;
504 string str;
505 size_t found;
507 // Gtk.Entry does not like the thousands separator, so we have to
508 // remove it after conversion from float to string.
510 stream.imbue(std::locale(""));
511 stream.precision(spinner.get_digits());
513 stream << fixed << exp(spinner.get_adjustment()->get_value());
515 str=stream.str();
517 // find thousands separators, remove them
518 found = str.find(use_facet<numpunct<char> >(std::locale("")).thousands_sep());
519 while(found != str.npos) {
520 str.erase(found,1);
522 //find next
523 found = str.find(use_facet<numpunct<char> >(std::locale("")).thousands_sep());
526 Entry *entry = dynamic_cast<Entry *>(&spinner);
527 entry->set_text(str);
529 return true;