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;
252 fract = scaling * (delta / (darea.get_width() - 2));
253 fract = min (1.0, fract);
254 fract = max (-1.0, fract);
255 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
266 BarController::expose (GdkEventExpose* /*event*/)
268 Glib::RefPtr<Gdk::Window> win (darea.get_window());
270 gint x1=0, x2=0, y1=0, y2=0;
274 fract = ((adjustment.get_value() - adjustment.get_lower()) /
275 (adjustment.get_upper() - adjustment.get_lower()));
279 w = darea.get_width() - 1;
280 h = darea.get_height();
281 x1 = (gint) floor (w * fract);
287 parent = get_parent();
290 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
292 0, 0, darea.get_width(), darea.get_height());
297 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
299 0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
302 win->draw_line (get_style()->get_fg_gc (get_state()), x1, 0, x1, h);
306 w = darea.get_width();
307 h = darea.get_height()-2;
309 parent = get_parent();
311 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
313 0, 0, darea.get_width(), darea.get_height());
315 win->draw_rectangle (parent->get_style()->get_bg_gc (parent->get_state()),
317 0, 0, darea.get_width(), darea.get_height());
320 x1 = (w/2) - ((w*fract)/2); // center, back up half the bar width
321 win->draw_rectangle (get_style()->get_fg_gc (get_state()), true, x1, 1, w*fract, h);
326 w = darea.get_width() - 2;
327 h = darea.get_height() - 2;
330 x2 = (gint) floor (w * fract);
334 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
336 0, 0, darea.get_width() - 1, darea.get_height() - 1);
338 /* draw active box */
340 win->draw_rectangle (get_style()->get_fg_gc (get_state()),
347 /* draw inactive box */
349 win->draw_rectangle (get_style()->get_fg_gc (STATE_INSENSITIVE),
369 std::string const label = get_label (xpos);
371 if (!label.empty()) {
373 layout->set_text (label);
376 layout->get_pixel_size (width, height);
379 xpos = max (3, 1 + (x2 - (width/2)));
380 xpos = min (darea.get_width() - width - 3, xpos);
383 win->draw_layout (get_style()->get_text_gc (get_state()),
385 (darea.get_height()/2) - (height/2),
393 BarController::set_style (barStyle s)
400 BarController::switch_to_bar ()
408 if (get_child() == &darea) {
418 SpinnerActive (false); /* EMIT SIGNAL */
424 BarController::switch_to_spinner ()
432 if (get_child() == &spinner) {
439 spinner.select_region (0, spinner.get_text_length());
440 spinner.grab_focus ();
444 SpinnerActive (true); /* EMIT SIGNAL */
450 BarController::entry_activated ()
456 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
463 BarController::set_use_parent (bool yn)
470 BarController::set_sensitive (bool yn)
472 Frame::set_sensitive (yn);
473 darea.set_sensitive (yn);
477 This is called when we need to update the adjustment with the value
478 from the spinner's text entry.
480 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
482 If we're not in logarithmic mode we can return false to use the
485 In theory we should check for conversion errors but set numeric
486 mode to true on the spinner prevents invalid input.
489 BarController::entry_input (double* new_value)
495 // extract a double from the string and take its log
496 Entry *entry = dynamic_cast<Entry *>(&spinner);
500 // Switch to user's preferred locale so that
501 // if they use different LC_NUMERIC conventions,
502 // we will honor them.
504 PBD::LocaleGuard lg ("");
505 sscanf (entry->get_text().c_str(), "%lf", &value);
508 *new_value = log(value);
514 This is called when we need to update the spinner's text entry
515 with the value of the adjustment.
517 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
519 If we're not in logarithmic mode we can return false to use the
523 BarController::entry_output ()
529 // generate the exponential and turn it into a string
530 // convert to correct locale.
538 // Switch to user's preferred locale so that
539 // if they use different LC_NUMERIC conventions,
540 // we will honor them.
542 PBD::LocaleGuard lg ("");
543 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
546 Entry *entry = dynamic_cast<Entry *>(&spinner);
547 entry->set_text(buf);