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.
27 #include <pbd/controllable.h>
29 #include <gtkmm2ext/gtk_ui.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/barcontroller.h>
37 using namespace Gtkmm2ext;
39 BarController::BarController (Gtk::Adjustment& adj,
40 boost::shared_ptr<PBD::Controllable> mc)
50 switch_on_release = false;
54 layout = darea.create_pango_layout("");
56 set_shadow_type (SHADOW_NONE);
58 initial_value = adjustment.get_value ();
60 adjustment.signal_value_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
61 adjustment.signal_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
63 darea.add_events (Gdk::BUTTON_RELEASE_MASK|
64 Gdk::BUTTON_PRESS_MASK|
65 Gdk::POINTER_MOTION_MASK|
66 Gdk::ENTER_NOTIFY_MASK|
67 Gdk::LEAVE_NOTIFY_MASK|
70 darea.signal_expose_event().connect (mem_fun (*this, &BarController::expose));
71 darea.signal_motion_notify_event().connect (mem_fun (*this, &BarController::motion));
72 darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press), false);
73 darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release), false);
74 darea.signal_scroll_event().connect (mem_fun (*this, &BarController::scroll));
76 spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
77 spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
78 spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
79 spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
80 spinner.set_digits (3);
81 spinner.set_numeric (true);
88 BarController::drop_grab ()
92 darea.remove_modal_grab();
98 BarController::button_press (GdkEventButton* ev)
102 if (binding_proxy.button_press_handler (ev)) {
106 switch (ev->button) {
108 if (ev->type == GDK_2BUTTON_PRESS) {
109 switch_on_release = true;
112 switch_on_release = false;
113 darea.add_modal_grab();
116 grab_window = ev->window;
123 fract = ev->x / (darea.get_width() - 2.0);
124 adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower()));
138 BarController::button_release (GdkEventButton* ev)
142 switch (ev->button) {
144 if (switch_on_release) {
145 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
149 if ((ev->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == GDK_SHIFT_MASK) {
150 adjustment.set_value (initial_value);
154 if ((ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
156 } else if (ev->state & GDK_CONTROL_MASK) {
162 mouse_control (ev->x, ev->window, scale);
180 BarController::scroll (GdkEventScroll* ev)
184 if ((ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
186 } else if (ev->state & GDK_CONTROL_MASK) {
192 switch (ev->direction) {
194 case GDK_SCROLL_RIGHT:
195 adjustment.set_value (adjustment.get_value() + (scale * adjustment.get_step_increment()));
198 case GDK_SCROLL_DOWN:
199 case GDK_SCROLL_LEFT:
200 adjustment.set_value (adjustment.get_value() - (scale * adjustment.get_step_increment()));
208 BarController::motion (GdkEventMotion* ev)
216 if ((ev->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == GDK_SHIFT_MASK) {
220 if ((ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
222 } else if (ev->state & GDK_CONTROL_MASK) {
228 return mouse_control (ev->x, ev->window, scale);
232 BarController::mouse_control (double x, GdkWindow* window, double scaling)
237 if (window != grab_window) {
239 grab_window = window;
249 fract = scaling * (delta / (darea.get_width() - 2));
250 fract = min (1.0, fract);
251 fract = max (-1.0, fract);
252 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
264 BarController::expose (GdkEventExpose* /*event*/)
266 Glib::RefPtr<Gdk::Window> win (darea.get_window());
268 gint x1=0, x2=0, y1=0, y2=0;
272 fract = ((adjustment.get_value() - adjustment.get_lower()) /
273 (adjustment.get_upper() - adjustment.get_lower()));
277 w = darea.get_width() - 1;
278 h = darea.get_height();
279 x1 = (gint) floor (w * fract);
285 parent = get_parent();
288 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
290 0, 0, darea.get_width(), darea.get_height());
295 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
297 0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
300 win->draw_line (get_style()->get_fg_gc (get_state()), x1, 0, x1, h);
308 w = darea.get_width() - 2;
309 h = darea.get_height() - 2;
312 x2 = (gint) floor (w * fract);
316 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
318 0, 0, darea.get_width() - 1, darea.get_height() - 1);
320 /* draw active box */
322 win->draw_rectangle (get_style()->get_fg_gc (get_state()),
329 /* draw inactive box */
331 win->draw_rectangle (get_style()->get_fg_gc (STATE_INSENSITIVE),
351 std::string const label = get_label (xpos);
353 if (!label.empty()) {
355 layout->set_text (label);
358 layout->get_pixel_size (width, height);
361 xpos = max (3, 1 + (x2 - (width/2)));
362 xpos = min (darea.get_width() - width - 3, xpos);
365 win->draw_layout (get_style()->get_text_gc (get_state()),
367 (darea.get_height()/2) - (height/2),
375 BarController::set_style (barStyle s)
382 BarController::switch_to_bar ()
390 if (get_child() == &darea) {
403 BarController::switch_to_spinner ()
411 if (get_child() == &spinner) {
418 spinner.select_region (0, spinner.get_text_length());
419 spinner.grab_focus ();
426 BarController::entry_activated ()
432 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
439 BarController::set_use_parent (bool yn)
446 BarController::set_sensitive (bool yn)
448 Frame::set_sensitive (yn);
449 darea.set_sensitive (yn);
453 This is called when we need to update the adjustment with the value
454 from the spinner's text entry.
456 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
458 If we're not in logarithmic mode we can return false to use the
461 In theory we should check for conversion errors but set numeric
462 mode to true on the spinner prevents invalid input.
465 BarController::entry_input (double* new_value)
471 // extract a double from the string and take its log
472 Entry *entry = dynamic_cast<Entry *>(&spinner);
473 stringstream stream(entry->get_text());
474 stream.imbue(std::locale(""));
479 *new_value = log(value);
484 This is called when we need to update the spinner's text entry
485 with the value of the adjustment.
487 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
489 If we're not in logarithmic mode we can return false to use the
493 BarController::entry_output ()
499 // generate the exponential and turn it into a string
500 // convert to correct locale.
506 // Gtk.Entry does not like the thousands separator, so we have to
507 // remove it after conversion from float to string.
509 stream.imbue(std::locale(""));
510 stream.precision(spinner.get_digits());
512 stream << fixed << exp(spinner.get_adjustment()->get_value());
516 // find thousands separators, remove them
517 found = str.find(use_facet<numpunct<char> >(std::locale("")).thousands_sep());
518 while(found != str.npos) {
522 found = str.find(use_facet<numpunct<char> >(std::locale("")).thousands_sep());
525 Entry *entry = dynamic_cast<Entry *>(&spinner);
526 entry->set_text(str);