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 (9);
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());
270 Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
273 gint x1=0, x2=0, y1=0, y2=0;
278 fract = ((adjustment.get_value() - adjustment.get_lower()) /
279 (adjustment.get_upper() - adjustment.get_lower()));
283 w = darea.get_width() - 1;
284 h = darea.get_height();
285 x1 = (gint) floor (w * fract);
291 parent = get_parent();
294 c = parent->get_style()->get_fg (parent->get_state());
296 g = c.get_green_p ();
298 context->set_source_rgb (r, g, b);
299 context->rectangle (0, 0, darea.get_width(), darea.get_height());
305 c = get_style()->get_bg (get_state());
307 g = c.get_green_p ();
309 context->set_source_rgb (r, g, b);
310 context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
314 c = get_style()->get_fg (get_state());
316 g = c.get_green_p ();
318 context->set_source_rgb (r, g, b);
319 context->move_to (x1, 0);
320 context->line_to (x1, h);
325 w = darea.get_width() - 1;
326 h = darea.get_height();
327 x1 = (gint) floor (w * fract);
331 parent = get_parent();
334 c = parent->get_style()->get_fg (parent->get_state());
336 g = c.get_green_p ();
338 context->set_source_rgb (r, g, b);
339 context->rectangle (0, 0, darea.get_width(), darea.get_height());
345 c = get_style()->get_bg (get_state());
347 g = c.get_green_p ();
349 context->set_source_rgb (r, g, b);
350 context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
354 c = get_style()->get_fg (get_state());
356 g = c.get_green_p ();
358 context->arc (x1, ((h-2)/2)-1, x2, 0, 2*M_PI);
362 w = darea.get_width();
363 h = darea.get_height()-2;
365 parent = get_parent();
367 c = parent->get_style()->get_fg (parent->get_state());
369 g = c.get_green_p ();
371 context->set_source_rgb (r, g, b);
372 context->rectangle (0, 0, darea.get_width(), darea.get_height());
376 c = get_style()->get_bg (get_state());
378 g = c.get_green_p ();
380 context->set_source_rgb (r, g, b);
381 context->rectangle (0, 0, darea.get_width(), darea.get_height());
384 c = get_style()->get_fg (get_state());
386 g = c.get_green_p ();
388 x1 = (w/2) - ((w*fract)/2); // center, back up half the bar width
389 context->set_source_rgb (r, g, b);
390 context->rectangle (x1, 1, w*fract, h);
396 w = darea.get_width() - 2;
397 h = darea.get_height() - 2;
400 x2 = (gint) floor (w * fract);
406 c = get_style()->get_bg (get_state());
408 g = c.get_green_p ();
410 context->set_source_rgb (r, g, b);
411 rounded_rectangle (context, 0, 0, darea.get_width(), darea.get_height());
414 /* draw active box */
416 c = get_style()->get_fg (get_state());
418 g = c.get_green_p ();
420 context->set_source_rgb (r, g, b);
421 rounded_rectangle (context, 1 + x1, 1 + y1, x2, y2);
424 /* draw inactive box */
426 c = get_style()->get_fg (STATE_INSENSITIVE);
428 g = c.get_green_p ();
430 context->set_source_rgb (r, g, b);
431 rounded_rectangle (context, 1 + x2, 1 + y1, w - x2, y2);
447 std::string const label = get_label (xpos);
449 if (!label.empty()) {
451 layout->set_text (label);
453 int width, height, x;
454 layout->get_pixel_size (width, height);
457 x = max (3, 1 + (x2 - (width/2)));
458 x = min (darea.get_width() - width - 3, (int) lrint (xpos));
460 x = lrint (darea.get_width() * xpos);
463 c = get_style()->get_text (get_state());
465 g = c.get_green_p ();
467 context->set_source_rgb (r, g, b);
468 context->move_to (x, (darea.get_height()/2) - (height/2));
469 layout->show_in_cairo_context (context);
476 BarController::set_style (barStyle s)
483 BarController::switch_to_bar ()
491 if (get_child() == &darea) {
501 SpinnerActive (false); /* EMIT SIGNAL */
507 BarController::switch_to_spinner ()
515 if (get_child() == &spinner) {
522 spinner.select_region (0, spinner.get_text_length());
523 spinner.grab_focus ();
527 SpinnerActive (true); /* EMIT SIGNAL */
533 BarController::entry_activated ()
539 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
546 BarController::set_use_parent (bool yn)
553 BarController::set_sensitive (bool yn)
555 Frame::set_sensitive (yn);
556 darea.set_sensitive (yn);
560 This is called when we need to update the adjustment with the value
561 from the spinner's text entry.
563 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
565 If we're not in logarithmic mode we can return false to use the
568 In theory we should check for conversion errors but set numeric
569 mode to true on the spinner prevents invalid input.
572 BarController::entry_input (double* new_value)
578 // extract a double from the string and take its log
579 Entry *entry = dynamic_cast<Entry *>(&spinner);
583 // Switch to user's preferred locale so that
584 // if they use different LC_NUMERIC conventions,
585 // we will honor them.
587 PBD::LocaleGuard lg ("");
588 sscanf (entry->get_text().c_str(), "%lf", &value);
591 *new_value = log(value);
597 This is called when we need to update the spinner's text entry
598 with the value of the adjustment.
600 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
602 If we're not in logarithmic mode we can return false to use the
606 BarController::entry_output ()
612 // generate the exponential and turn it into a string
613 // convert to correct locale.
621 // Switch to user's preferred locale so that
622 // if they use different LC_NUMERIC conventions,
623 // we will honor them.
625 PBD::LocaleGuard lg ("");
626 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
629 Entry *entry = dynamic_cast<Entry *>(&spinner);
630 entry->set_text(buf);