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);
454 /* if (!label.empty()) {
456 layout->set_text (label);
458 int width, height, x;
459 layout->get_pixel_size (width, height);
462 x = max (3, 1 + (x2 - (width/2)));
463 x = min (darea.get_width() - width - 3, (int) lrint (xpos));
465 x = lrint (darea.get_width() * xpos);
468 c = get_style()->get_text (get_state());
470 g = c.get_green_p ();
472 context->set_source_rgb (r, g, b);
473 context->move_to (x, (darea.get_height()/2) - (height/2));
474 layout->show_in_cairo_context (context);
481 BarController::set_style (barStyle s)
488 BarController::switch_to_bar ()
496 if (get_child() == &darea) {
506 SpinnerActive (false); /* EMIT SIGNAL */
512 BarController::switch_to_spinner ()
520 if (get_child() == &spinner) {
527 spinner.select_region (0, spinner.get_text_length());
528 spinner.grab_focus ();
532 SpinnerActive (true); /* EMIT SIGNAL */
538 BarController::entry_activated ()
544 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
551 BarController::set_use_parent (bool yn)
558 BarController::set_sensitive (bool yn)
560 Frame::set_sensitive (yn);
561 darea.set_sensitive (yn);
565 This is called when we need to update the adjustment with the value
566 from the spinner's text entry.
568 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
570 If we're not in logarithmic mode we can return false to use the
573 In theory we should check for conversion errors but set numeric
574 mode to true on the spinner prevents invalid input.
577 BarController::entry_input (double* new_value)
583 // extract a double from the string and take its log
584 Entry *entry = dynamic_cast<Entry *>(&spinner);
588 // Switch to user's preferred locale so that
589 // if they use different LC_NUMERIC conventions,
590 // we will honor them.
592 PBD::LocaleGuard lg ("");
593 sscanf (entry->get_text().c_str(), "%lf", &value);
596 *new_value = log(value);
602 This is called when we need to update the spinner's text entry
603 with the value of the adjustment.
605 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
607 If we're not in logarithmic mode we can return false to use the
611 BarController::entry_output ()
617 // generate the exponential and turn it into a string
618 // convert to correct locale.
626 // Switch to user's preferred locale so that
627 // if they use different LC_NUMERIC conventions,
628 // we will honor them.
630 PBD::LocaleGuard lg ("");
631 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
634 Entry *entry = dynamic_cast<Entry *>(&spinner);
635 entry->set_text(buf);