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>
28 #include <pbd/locale_guard.h>
30 #include "gtkmm2ext/gtk_ui.h"
31 #include "gtkmm2ext/utils.h"
32 #include "gtkmm2ext/keyboard.h"
33 #include "gtkmm2ext/barcontroller.h"
39 using namespace Gtkmm2ext;
41 BarController::BarController (Gtk::Adjustment& adj,
42 boost::shared_ptr<PBD::Controllable> mc)
52 switch_on_release = false;
56 layout = darea.create_pango_layout("");
58 set_shadow_type (SHADOW_NONE);
60 initial_value = adjustment.get_value ();
62 adjustment.signal_value_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
63 adjustment.signal_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
65 darea.add_events (Gdk::BUTTON_RELEASE_MASK|
66 Gdk::BUTTON_PRESS_MASK|
67 Gdk::POINTER_MOTION_MASK|
68 Gdk::ENTER_NOTIFY_MASK|
69 Gdk::LEAVE_NOTIFY_MASK|
72 darea.signal_expose_event().connect (mem_fun (*this, &BarController::expose));
73 darea.signal_motion_notify_event().connect (mem_fun (*this, &BarController::motion));
74 darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press), false);
75 darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release), false);
76 darea.signal_scroll_event().connect (mem_fun (*this, &BarController::scroll));
78 spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
79 spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
80 spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
81 spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
82 spinner.set_digits (3);
83 spinner.set_numeric (true);
90 BarController::drop_grab ()
94 darea.remove_modal_grab();
100 BarController::button_press (GdkEventButton* ev)
104 if (binding_proxy.button_press_handler (ev)) {
108 switch (ev->button) {
110 if (ev->type == GDK_2BUTTON_PRESS) {
111 switch_on_release = true;
114 switch_on_release = false;
115 darea.add_modal_grab();
118 grab_window = ev->window;
125 fract = ev->x / (darea.get_width() - 2.0);
126 adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower()));
140 BarController::button_release (GdkEventButton* ev)
144 switch (ev->button) {
146 if (switch_on_release) {
147 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
151 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
152 adjustment.set_value (initial_value);
156 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
158 } else if (ev->state & Keyboard::PrimaryModifier) {
164 mouse_control (ev->x, ev->window, scale);
182 BarController::scroll (GdkEventScroll* ev)
186 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
188 } else if (ev->state & Keyboard::PrimaryModifier) {
194 switch (ev->direction) {
196 case GDK_SCROLL_RIGHT:
197 adjustment.set_value (adjustment.get_value() + (scale * adjustment.get_step_increment()));
200 case GDK_SCROLL_DOWN:
201 case GDK_SCROLL_LEFT:
202 adjustment.set_value (adjustment.get_value() - (scale * adjustment.get_step_increment()));
210 BarController::motion (GdkEventMotion* ev)
218 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
222 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
224 } else if (ev->state & Keyboard::PrimaryModifier) {
230 return mouse_control (ev->x, ev->window, scale);
234 BarController::mouse_control (double x, GdkWindow* window, double scaling)
239 if (window != grab_window) {
241 grab_window = window;
253 fract = scaling * (delta / (darea.get_width() - 2));
254 fract = min (1.0, fract);
255 fract = max (-1.0, fract);
256 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
267 BarController::expose (GdkEventExpose* /*event*/)
269 Glib::RefPtr<Gdk::Window> win (darea.get_window());
271 gint x1=0, x2=0, y1=0, y2=0;
275 fract = ((adjustment.get_value() - adjustment.get_lower()) /
276 (adjustment.get_upper() - adjustment.get_lower()));
280 w = darea.get_width() - 1;
281 h = darea.get_height();
282 x1 = (gint) floor (w * fract);
288 parent = get_parent();
291 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
293 0, 0, darea.get_width(), darea.get_height());
298 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
300 0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
303 win->draw_line (get_style()->get_fg_gc (get_state()), x1, 0, x1, h);
307 w = darea.get_width() - 1;
308 h = darea.get_height();
309 x1 = (gint) floor (w * fract);
313 parent = get_parent();
316 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
318 0, 0, darea.get_width(), darea.get_height());
323 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
325 0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
328 win->draw_arc (get_style()->get_fg_gc (get_state()), true, x1, ((h-2)/2)-1, x2, x2, 0, 360 * 64);
332 w = darea.get_width();
333 h = darea.get_height()-2;
335 parent = get_parent();
337 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
339 0, 0, darea.get_width(), darea.get_height());
341 win->draw_rectangle (parent->get_style()->get_bg_gc (parent->get_state()),
343 0, 0, darea.get_width(), darea.get_height());
346 x1 = (w/2) - ((w*fract)/2); // center, back up half the bar width
347 win->draw_rectangle (get_style()->get_fg_gc (get_state()), true, x1, 1, w*fract, h);
352 w = darea.get_width() - 2;
353 h = darea.get_height() - 2;
356 x2 = (gint) floor (w * fract);
360 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
362 0, 0, darea.get_width() - 1, darea.get_height() - 1);
364 /* draw active box */
366 win->draw_rectangle (get_style()->get_fg_gc (get_state()),
373 /* draw inactive box */
375 win->draw_rectangle (get_style()->get_fg_gc (STATE_INSENSITIVE),
395 std::string const label = get_label (xpos);
397 if (!label.empty()) {
399 layout->set_text (label);
402 layout->get_pixel_size (width, height);
405 xpos = max (3, 1 + (x2 - (width/2)));
406 xpos = min (darea.get_width() - width - 3, xpos);
409 win->draw_layout (get_style()->get_text_gc (get_state()),
411 (darea.get_height()/2) - (height/2),
419 BarController::set_style (barStyle s)
426 BarController::switch_to_bar ()
434 if (get_child() == &darea) {
444 SpinnerActive (false); /* EMIT SIGNAL */
450 BarController::switch_to_spinner ()
458 if (get_child() == &spinner) {
465 spinner.select_region (0, spinner.get_text_length());
466 spinner.grab_focus ();
470 SpinnerActive (true); /* EMIT SIGNAL */
476 BarController::entry_activated ()
482 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
489 BarController::set_use_parent (bool yn)
496 BarController::set_sensitive (bool yn)
498 Frame::set_sensitive (yn);
499 darea.set_sensitive (yn);
503 This is called when we need to update the adjustment with the value
504 from the spinner's text entry.
506 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
508 If we're not in logarithmic mode we can return false to use the
511 In theory we should check for conversion errors but set numeric
512 mode to true on the spinner prevents invalid input.
515 BarController::entry_input (double* new_value)
521 // extract a double from the string and take its log
522 Entry *entry = dynamic_cast<Entry *>(&spinner);
526 // Switch to user's preferred locale so that
527 // if they use different LC_NUMERIC conventions,
528 // we will honor them.
530 PBD::LocaleGuard lg ("");
531 sscanf (entry->get_text().c_str(), "%lf", &value);
534 *new_value = log(value);
540 This is called when we need to update the spinner's text entry
541 with the value of the adjustment.
543 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
545 If we're not in logarithmic mode we can return false to use the
549 BarController::entry_output ()
555 // generate the exponential and turn it into a string
556 // convert to correct locale.
564 // Switch to user's preferred locale so that
565 // if they use different LC_NUMERIC conventions,
566 // we will honor them.
568 PBD::LocaleGuard lg ("");
569 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
572 Entry *entry = dynamic_cast<Entry *>(&spinner);
573 entry->set_text(buf);