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::~BarController ()
93 // delete shine_pattern;
97 BarController::drop_grab ()
101 darea.remove_modal_grab();
107 BarController::button_press (GdkEventButton* ev)
111 if (binding_proxy.button_press_handler (ev)) {
115 switch (ev->button) {
117 if (ev->type == GDK_2BUTTON_PRESS) {
118 switch_on_release = true;
121 switch_on_release = false;
122 darea.add_modal_grab();
125 grab_window = ev->window;
132 fract = ev->x / (darea.get_width() - 2.0);
133 adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower()));
147 BarController::button_release (GdkEventButton* ev)
151 switch (ev->button) {
153 if (switch_on_release) {
154 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
158 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
159 adjustment.set_value (initial_value);
163 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
165 } else if (ev->state & Keyboard::PrimaryModifier) {
171 mouse_control (ev->x, ev->window, scale);
189 BarController::scroll (GdkEventScroll* ev)
193 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
195 } else if (ev->state & Keyboard::PrimaryModifier) {
201 switch (ev->direction) {
203 case GDK_SCROLL_RIGHT:
204 adjustment.set_value (adjustment.get_value() + (scale * adjustment.get_step_increment()));
207 case GDK_SCROLL_DOWN:
208 case GDK_SCROLL_LEFT:
209 adjustment.set_value (adjustment.get_value() - (scale * adjustment.get_step_increment()));
217 BarController::motion (GdkEventMotion* ev)
225 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
229 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
231 } else if (ev->state & Keyboard::PrimaryModifier) {
237 return mouse_control (ev->x, ev->window, scale);
241 BarController::mouse_control (double x, GdkWindow* window, double scaling)
246 if (window != grab_window) {
248 grab_window = window;
260 fract = scaling * (delta / (darea.get_width() - 2));
261 fract = min (1.0, fract);
262 fract = max (-1.0, fract);
263 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
274 BarController::create_patterns ()
276 Glib::RefPtr<Gdk::Window> win (darea.get_window());
277 Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
279 Gdk::Color c = get_style()->get_fg (get_state());
282 g = c.get_green_p ();
285 float rheight = darea.get_height()-2;
287 cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, rheight);
288 cairo_pattern_add_color_stop_rgba (pat, 0, r*0.8,g*0.8,b*0.8, 1.0);
289 cairo_pattern_add_color_stop_rgba (pat, 1, r*0.6,g*0.6,b*0.6, 1.0);
290 Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
292 cairo_pattern_destroy(pat);
294 pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, rheight);
295 cairo_pattern_add_color_stop_rgba (pat, 0, 1,1,1,0.0);
296 cairo_pattern_add_color_stop_rgba (pat, 0.2, 1,1,1,0.3);
297 cairo_pattern_add_color_stop_rgba (pat, 0.5, 1,1,1,0.0);
298 cairo_pattern_add_color_stop_rgba (pat, 1, 1,1,1,0.0);
299 Cairo::RefPtr<Cairo::Pattern> p2 (new Cairo::Pattern (pat, false));
301 cairo_pattern_destroy(pat);
306 BarController::expose (GdkEventExpose* /*event*/)
308 Glib::RefPtr<Gdk::Window> win (darea.get_window());
309 Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
316 gint x1=0, x2=0, y2=0;
318 double fract, radius;
321 fract = ((adjustment.get_value() - adjustment.get_lower()) /
322 (adjustment.get_upper() - adjustment.get_lower()));
326 w = darea.get_width() - 1;
327 h = darea.get_height();
328 x1 = (gint) floor (w * fract);
333 parent = get_parent();
336 c = parent->get_style()->get_fg (parent->get_state());
338 g = c.get_green_p ();
340 context->set_source_rgb (r, g, b);
341 context->rectangle (0, 0, darea.get_width(), darea.get_height());
347 c = get_style()->get_bg (get_state());
349 g = c.get_green_p ();
351 context->set_source_rgb (r, g, b);
352 context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
356 c = get_style()->get_fg (get_state());
358 g = c.get_green_p ();
360 context->set_source_rgb (r, g, b);
361 context->move_to (x1, 0);
362 context->line_to (x1, h);
367 w = darea.get_width() - 1;
368 h = darea.get_height();
369 x1 = (gint) floor (w * fract);
373 parent = get_parent();
376 c = parent->get_style()->get_fg (parent->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());
387 c = get_style()->get_bg (get_state());
389 g = c.get_green_p ();
391 context->set_source_rgb (r, g, b);
392 context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
396 c = get_style()->get_fg (get_state());
398 g = c.get_green_p ();
400 context->arc (x1, ((h-2)/2)-1, x2, 0, 2*M_PI);
404 w = darea.get_width();
405 h = darea.get_height()-2;
407 parent = get_parent();
409 c = parent->get_style()->get_fg (parent->get_state());
411 g = c.get_green_p ();
413 context->set_source_rgb (r, g, b);
414 context->rectangle (0, 0, darea.get_width(), darea.get_height());
418 c = get_style()->get_bg (get_state());
420 g = c.get_green_p ();
422 context->set_source_rgb (r, g, b);
423 context->rectangle (0, 0, darea.get_width(), darea.get_height());
426 c = get_style()->get_fg (get_state());
428 g = c.get_green_p ();
430 x1 = (w/2) - ((w*fract)/2); // center, back up half the bar width
431 context->set_source_rgb (r, g, b);
432 context->rectangle (x1, 1, w*fract, h);
438 w = darea.get_width() - 2;
439 h = darea.get_height() - 2;
441 x2 = (gint) floor (w * fract);
448 context->set_source_rgb (0,0,0);
449 cairo_rectangle (context->cobj(), 0, 0, darea.get_width(), darea.get_height());
452 /* draw active box */
454 context->set_source (pattern);
455 rounded_rectangle (context, 1, 1, x2, y2, radius-1.5);
458 // context->set_source (shine_pattern);
459 // rounded_rectangle (context, 2, 3, x2-2, y2-8, radius-2);
471 if (!darea.get_sensitive()) {
472 rounded_rectangle (context, 0, 0, darea.get_width(), darea.get_height(), 3);
473 context->set_source_rgba (0.505, 0.517, 0.525, 0.6);
480 std::string const label = get_label (xpos);
482 if (!label.empty()) {
484 layout->set_text (label);
486 int width, height, x;
487 layout->get_pixel_size (width, height);
490 x = max (3, 1 + (x2 - (width/2)));
491 x = min (darea.get_width() - width - 3, (int) lrint (xpos));
493 x = lrint (darea.get_width() * xpos);
496 c = get_style()->get_text (get_state());
498 g = c.get_green_p ();
500 context->set_source_rgb (r, g, b);
501 context->move_to (x, (darea.get_height()/2) - (height/2));
502 layout->show_in_cairo_context (context);
509 BarController::set_style (barStyle s)
516 BarController::switch_to_bar ()
524 if (get_child() == &darea) {
534 SpinnerActive (false); /* EMIT SIGNAL */
540 BarController::switch_to_spinner ()
548 if (get_child() == &spinner) {
555 spinner.select_region (0, spinner.get_text_length());
556 spinner.grab_focus ();
560 SpinnerActive (true); /* EMIT SIGNAL */
566 BarController::entry_activated ()
572 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
579 BarController::set_use_parent (bool yn)
586 BarController::set_sensitive (bool yn)
588 Frame::set_sensitive (yn);
589 darea.set_sensitive (yn);
593 This is called when we need to update the adjustment with the value
594 from the spinner's text entry.
596 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
598 If we're not in logarithmic mode we can return false to use the
601 In theory we should check for conversion errors but set numeric
602 mode to true on the spinner prevents invalid input.
605 BarController::entry_input (double* new_value)
611 // extract a double from the string and take its log
612 Entry *entry = dynamic_cast<Entry *>(&spinner);
616 // Switch to user's preferred locale so that
617 // if they use different LC_NUMERIC conventions,
618 // we will honor them.
620 PBD::LocaleGuard lg ("");
621 sscanf (entry->get_text().c_str(), "%lf", &value);
624 *new_value = log(value);
630 This is called when we need to update the spinner's text entry
631 with the value of the adjustment.
633 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
635 If we're not in logarithmic mode we can return false to use the
639 BarController::entry_output ()
645 // generate the exponential and turn it into a string
646 // convert to correct locale.
654 // Switch to user's preferred locale so that
655 // if they use different LC_NUMERIC conventions,
656 // we will honor them.
658 PBD::LocaleGuard lg ("");
659 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
662 Entry *entry = dynamic_cast<Entry *>(&spinner);
663 entry->set_text(buf);