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"
34 #include "gtkmm2ext/cairo_widget.h"
40 using namespace Gtkmm2ext;
42 BarController::BarController (Gtk::Adjustment& adj,
43 boost::shared_ptr<PBD::Controllable> mc)
48 , spinner (adjustment)
53 switch_on_release = false;
57 layout = darea.create_pango_layout("");
59 set_shadow_type (SHADOW_NONE);
61 initial_value = adjustment.get_value ();
63 adjustment.signal_value_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
64 adjustment.signal_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
66 darea.add_events (Gdk::BUTTON_RELEASE_MASK|
67 Gdk::BUTTON_PRESS_MASK|
68 Gdk::POINTER_MOTION_MASK|
69 Gdk::ENTER_NOTIFY_MASK|
70 Gdk::LEAVE_NOTIFY_MASK|
73 darea.signal_expose_event().connect (mem_fun (*this, &BarController::expose));
74 darea.signal_motion_notify_event().connect (mem_fun (*this, &BarController::motion));
75 darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press), false);
76 darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release), false);
77 darea.signal_scroll_event().connect (mem_fun (*this, &BarController::scroll));
78 darea.signal_enter_notify_event().connect (mem_fun (*this, &BarController::on_enter_notify_event));
79 darea.signal_leave_notify_event().connect (mem_fun (*this, &BarController::on_leave_notify_event));
81 spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
82 spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
83 spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
84 spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
85 spinner.set_digits (9);
86 spinner.set_numeric (true);
93 BarController::~BarController ()
98 BarController::on_enter_notify_event (GdkEventCrossing*)
101 Keyboard::magic_widget_grab_focus ();
107 BarController::on_leave_notify_event (GdkEventCrossing*)
110 Keyboard::magic_widget_drop_focus();
116 BarController::drop_grab ()
120 darea.remove_modal_grab();
126 BarController::button_press (GdkEventButton* ev)
130 if (binding_proxy.button_press_handler (ev)) {
134 switch (ev->button) {
136 if (ev->type == GDK_2BUTTON_PRESS) {
137 switch_on_release = true;
140 switch_on_release = false;
141 darea.add_modal_grab();
144 grab_window = ev->window;
151 fract = ev->x / (darea.get_width() - 2.0);
152 adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower()));
166 BarController::button_release (GdkEventButton* ev)
170 switch (ev->button) {
172 if (switch_on_release) {
173 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
177 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
178 adjustment.set_value (initial_value);
182 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
184 } else if (ev->state & Keyboard::PrimaryModifier) {
190 mouse_control (ev->x, ev->window, scale);
208 BarController::scroll (GdkEventScroll* ev)
212 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
214 } else if (ev->state & Keyboard::PrimaryModifier) {
220 switch (ev->direction) {
222 case GDK_SCROLL_RIGHT:
223 adjustment.set_value (adjustment.get_value() + (scale * adjustment.get_step_increment()));
226 case GDK_SCROLL_DOWN:
227 case GDK_SCROLL_LEFT:
228 adjustment.set_value (adjustment.get_value() - (scale * adjustment.get_step_increment()));
236 BarController::motion (GdkEventMotion* ev)
244 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
248 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
250 } else if (ev->state & Keyboard::PrimaryModifier) {
256 return mouse_control (ev->x, ev->window, scale);
260 BarController::mouse_control (double x, GdkWindow* window, double scaling)
265 if (window != grab_window) {
267 grab_window = window;
279 fract = scaling * (delta / (darea.get_width() - 2));
280 fract = min (1.0, fract);
281 fract = max (-1.0, fract);
282 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
293 BarController::get_parent_bg ()
297 parent = get_parent ();
300 static const char* has_cairo_widget_background_info = "has_cairo_widget_background_info";
301 void* p = g_object_get_data (G_OBJECT(parent->gobj()), has_cairo_widget_background_info);
304 Glib::RefPtr<Gtk::Style> style = parent->get_style();
305 return style->get_bg (get_state());
308 if (!parent->get_has_window()) {
309 parent = parent->get_parent();
315 if (parent && parent->get_has_window()) {
316 return parent->get_style ()->get_bg (parent->get_state());
319 return get_style ()->get_bg (get_state());
323 BarController::expose (GdkEventExpose* /*event*/)
325 Glib::RefPtr<Gdk::Window> win (darea.get_window());
326 Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
327 cairo_t* cr = context->cobj();
329 Gdk::Color fg_col = get_style()->get_fg (get_state());
331 double fract = ((adjustment.get_value() - adjustment.get_lower()) /
332 (adjustment.get_upper() - adjustment.get_lower()));
334 gint w = darea.get_width() ;
335 gint h = darea.get_height();
336 gint bar_start, bar_width;
341 bar_start = (gint) floor ((w-1) * fract);
350 bar_width = (w*fract);
351 bar_start = (w/2) - bar_width/2; // center, back up half the bar width
356 bar_width = floor((w-2)*fract);
368 //fill in the bg rect ...
369 Gdk::Color c = get_parent_bg(); //get_style()->get_bg (Gtk::STATE_PRELIGHT); //why prelight? Shouldn't we be using the parent's color? maybe get_parent_bg ?
370 CairoWidget::set_source_rgb_a (cr, c);
371 cairo_rectangle (cr, 0, 0, w, h);
375 cairo_set_source_rgba (cr, 0.17, 0.17, 0.17, 1.0);
376 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius-0.5);
379 //mask off the corners
380 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius-0.5);
383 //background gradient
384 if ( !CairoWidget::flat_buttons() ) {
385 cairo_pattern_t *bg_gradient = cairo_pattern_create_linear (0.0, 0.0, 0, h);
386 cairo_pattern_add_color_stop_rgba (bg_gradient, 0, 0, 0, 0, 0.4);
387 cairo_pattern_add_color_stop_rgba (bg_gradient, 0.2, 0, 0, 0, 0.2);
388 cairo_pattern_add_color_stop_rgba (bg_gradient, 1, 0, 0, 0, 0.0);
389 cairo_set_source (cr, bg_gradient);
390 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius-1.5);
392 cairo_pattern_destroy(bg_gradient);
396 CairoWidget::set_source_rgb_a (cr, fg_col, 1.0);
397 Gtkmm2ext::rounded_rectangle (cr, bar_start, 1, bar_width, h-2, radius - 1.5);
401 if (!CairoWidget::flat_buttons() ) {
402 cairo_pattern_t * fg_gradient = cairo_pattern_create_linear (0.0, 0.0, 0, h);
403 cairo_pattern_add_color_stop_rgba (fg_gradient, 0, 0, 0, 0, 0.0);
404 cairo_pattern_add_color_stop_rgba (fg_gradient, 0.1, 0, 0, 0, 0.0);
405 cairo_pattern_add_color_stop_rgba (fg_gradient, 1, 0, 0, 0, 0.3);
406 cairo_set_source (cr, fg_gradient);
407 Gtkmm2ext::rounded_rectangle (cr, bar_start, 1, bar_width, h-2, radius - 1.5);
409 cairo_pattern_destroy(fg_gradient);
412 cairo_reset_clip(cr);
415 cairo_set_line_width (cr, 1.0);
416 cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
417 Gtkmm2ext::rounded_rectangle (cr, 0.5, 0.5, w-1, h-1, radius);
420 /* draw the unity-position line if it's not at either end*/
421 /* if (unity_loc > 0) {
422 context->set_line_width (1);
423 cairo_set_source_rgba (cr, 1,1,1, 1.0);
424 if ( _orien == VERT) {
425 if (unity_loc < h ) {
426 context->move_to (2.5, unity_loc + radius + .5);
427 context->line_to (girth-2.5, unity_loc + radius + .5);
431 if ( unity_loc < w ){
432 context->move_to (unity_loc - radius + .5, 3.5);
433 context->line_to (unity_loc - radius + .5, girth-3.5);
439 if (!darea.get_sensitive()) {
440 rounded_rectangle (context, 0, 0, darea.get_width(), darea.get_height(), 3);
441 context->set_source_rgba (0.505, 0.517, 0.525, 0.6);
443 } else if (_hovering) {
444 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius);
445 cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.1);
452 std::string const label = get_label (xpos);
453 if (!label.empty()) {
456 layout->set_text (label);
457 layout->get_pixel_size (twidth, theight);
460 cairo_set_source_rgba (cr, 0.17, 0.17, 0.17, 1.0);
461 context->move_to ( 5, (darea.get_height()/2) - (theight/2));
463 c = get_style()->get_text (get_state());
464 CairoWidget::set_source_rgb_a (cr, c, 0.7);
465 context->move_to ( w-twidth-5, (darea.get_height()/2) - (theight/2));
467 layout->show_in_cairo_context (context);
474 BarController::set_style (barStyle s)
481 BarController::switch_to_bar ()
489 if (get_child() == &darea) {
499 SpinnerActive (false); /* EMIT SIGNAL */
505 BarController::switch_to_spinner ()
513 if (get_child() == &spinner) {
520 spinner.select_region (0, spinner.get_text_length());
521 spinner.grab_focus ();
525 SpinnerActive (true); /* EMIT SIGNAL */
531 BarController::entry_activated ()
537 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
544 BarController::set_use_parent (bool yn)
551 BarController::set_sensitive (bool yn)
553 Frame::set_sensitive (yn);
554 darea.set_sensitive (yn);
558 This is called when we need to update the adjustment with the value
559 from the spinner's text entry.
561 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
563 If we're not in logarithmic mode we can return false to use the
566 In theory we should check for conversion errors but set numeric
567 mode to true on the spinner prevents invalid input.
570 BarController::entry_input (double* new_value)
576 // extract a double from the string and take its log
577 Entry *entry = dynamic_cast<Entry *>(&spinner);
581 // Switch to user's preferred locale so that
582 // if they use different LC_NUMERIC conventions,
583 // we will honor them.
585 PBD::LocaleGuard lg ("");
586 sscanf (entry->get_text().c_str(), "%lf", &value);
589 *new_value = log(value);
595 This is called when we need to update the spinner's text entry
596 with the value of the adjustment.
598 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
600 If we're not in logarithmic mode we can return false to use the
604 BarController::entry_output ()
610 // generate the exponential and turn it into a string
611 // convert to correct locale.
619 // Switch to user's preferred locale so that
620 // if they use different LC_NUMERIC conventions,
621 // we will honor them.
623 PBD::LocaleGuard lg ("");
624 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
627 Entry *entry = dynamic_cast<Entry *>(&spinner);
628 entry->set_text(buf);