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)
47 , spinner (adjustment)
53 switch_on_release = false;
57 layout = darea.create_pango_layout("");
59 set (.5, .5, 1.0, 1.0);
62 initial_value = adjustment.get_value ();
64 adjustment.signal_value_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
65 adjustment.signal_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
67 darea.add_events (Gdk::BUTTON_RELEASE_MASK|
68 Gdk::BUTTON_PRESS_MASK|
69 Gdk::POINTER_MOTION_MASK|
70 Gdk::ENTER_NOTIFY_MASK|
71 Gdk::LEAVE_NOTIFY_MASK|
74 darea.signal_expose_event().connect (mem_fun (*this, &BarController::expose));
75 darea.signal_motion_notify_event().connect (mem_fun (*this, &BarController::motion));
76 darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press), false);
77 darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release), false);
78 darea.signal_scroll_event().connect (mem_fun (*this, &BarController::scroll));
79 darea.signal_enter_notify_event().connect (mem_fun (*this, &BarController::on_enter_notify_event));
80 darea.signal_leave_notify_event().connect (mem_fun (*this, &BarController::on_leave_notify_event));
82 spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
83 spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
84 spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
85 spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
86 spinner.set_digits (9);
87 spinner.set_numeric (true);
94 BarController::~BarController ()
99 BarController::on_enter_notify_event (GdkEventCrossing*)
102 Keyboard::magic_widget_grab_focus ();
108 BarController::on_leave_notify_event (GdkEventCrossing*)
111 Keyboard::magic_widget_drop_focus();
117 BarController::drop_grab ()
121 darea.remove_modal_grab();
127 BarController::button_press (GdkEventButton* ev)
131 if (binding_proxy.button_press_handler (ev)) {
135 switch (ev->button) {
137 if (ev->type == GDK_2BUTTON_PRESS) {
138 switch_on_release = true;
141 switch_on_release = false;
142 darea.add_modal_grab();
145 grab_window = ev->window;
152 fract = ev->x / (darea.get_width() - 2.0);
153 adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower()));
167 BarController::button_release (GdkEventButton* ev)
171 switch (ev->button) {
173 if (switch_on_release) {
174 Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
178 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
179 adjustment.set_value (initial_value);
183 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
185 } else if (ev->state & Keyboard::PrimaryModifier) {
191 mouse_control (ev->x, ev->window, scale);
209 BarController::scroll (GdkEventScroll* ev)
213 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
215 } else if (ev->state & Keyboard::PrimaryModifier) {
221 switch (ev->direction) {
223 case GDK_SCROLL_RIGHT:
224 adjustment.set_value (adjustment.get_value() + (scale * adjustment.get_step_increment()));
227 case GDK_SCROLL_DOWN:
228 case GDK_SCROLL_LEFT:
229 adjustment.set_value (adjustment.get_value() - (scale * adjustment.get_step_increment()));
237 BarController::motion (GdkEventMotion* ev)
245 if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
249 if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
251 } else if (ev->state & Keyboard::PrimaryModifier) {
257 return mouse_control (ev->x, ev->window, scale);
261 BarController::mouse_control (double x, GdkWindow* window, double scaling)
266 if (window != grab_window) {
268 grab_window = window;
280 fract = scaling * (delta / (darea.get_width() - 2));
281 fract = min (1.0, fract);
282 fract = max (-1.0, fract);
283 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
294 BarController::get_parent_bg ()
298 parent = get_parent ();
301 static const char* has_cairo_widget_background_info = "has_cairo_widget_background_info";
302 void* p = g_object_get_data (G_OBJECT(parent->gobj()), has_cairo_widget_background_info);
305 Glib::RefPtr<Gtk::Style> style = parent->get_style();
306 return style->get_bg (get_state());
309 if (!parent->get_has_window()) {
310 parent = parent->get_parent();
316 if (parent && parent->get_has_window()) {
317 return parent->get_style ()->get_bg (parent->get_state());
320 return get_style ()->get_bg (get_state());
324 BarController::expose (GdkEventExpose* /*event*/)
326 Glib::RefPtr<Gdk::Window> win (darea.get_window());
327 Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
328 cairo_t* cr = context->cobj();
330 Gdk::Color fg_col = get_style()->get_fg (get_state());
332 double fract = ((adjustment.get_value() - adjustment.get_lower()) /
333 (adjustment.get_upper() - adjustment.get_lower()));
335 gint w = darea.get_width() ;
336 gint h = darea.get_height();
337 gint bar_start, bar_width;
342 bar_start = (gint) floor ((w-1) * fract);
351 bar_width = (w*fract);
352 bar_start = (w/2) - bar_width/2; // center, back up half the bar width
357 bar_width = floor((w-2)*fract);
369 //fill in the bg rect ...
370 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 ?
371 CairoWidget::set_source_rgb_a (cr, c);
372 cairo_rectangle (cr, 0, 0, w, h);
376 cairo_set_source_rgba (cr, 0.17, 0.17, 0.17, 1.0);
377 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius-0.5);
380 //mask off the corners
381 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius-0.5);
384 //background gradient
385 if ( !CairoWidget::flat_buttons() ) {
386 cairo_pattern_t *bg_gradient = cairo_pattern_create_linear (0.0, 0.0, 0, h);
387 cairo_pattern_add_color_stop_rgba (bg_gradient, 0, 0, 0, 0, 0.4);
388 cairo_pattern_add_color_stop_rgba (bg_gradient, 0.2, 0, 0, 0, 0.2);
389 cairo_pattern_add_color_stop_rgba (bg_gradient, 1, 0, 0, 0, 0.0);
390 cairo_set_source (cr, bg_gradient);
391 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius-1.5);
393 cairo_pattern_destroy(bg_gradient);
397 CairoWidget::set_source_rgb_a (cr, fg_col, 1.0);
398 Gtkmm2ext::rounded_rectangle (cr, bar_start, 1, bar_width, h-2, radius - 1.5);
402 if (!CairoWidget::flat_buttons() ) {
403 cairo_pattern_t * fg_gradient = cairo_pattern_create_linear (0.0, 0.0, 0, h);
404 cairo_pattern_add_color_stop_rgba (fg_gradient, 0, 0, 0, 0, 0.0);
405 cairo_pattern_add_color_stop_rgba (fg_gradient, 0.1, 0, 0, 0, 0.0);
406 cairo_pattern_add_color_stop_rgba (fg_gradient, 1, 0, 0, 0, 0.3);
407 cairo_set_source (cr, fg_gradient);
408 Gtkmm2ext::rounded_rectangle (cr, bar_start, 1, bar_width, h-2, radius - 1.5);
410 cairo_pattern_destroy(fg_gradient);
413 cairo_reset_clip(cr);
416 cairo_set_line_width (cr, 1.0);
417 cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
418 Gtkmm2ext::rounded_rectangle (cr, 0.5, 0.5, w-1, h-1, radius);
421 /* draw the unity-position line if it's not at either end*/
422 /* if (unity_loc > 0) {
423 context->set_line_width (1);
424 cairo_set_source_rgba (cr, 1,1,1, 1.0);
425 if ( _orien == VERT) {
426 if (unity_loc < h ) {
427 context->move_to (2.5, unity_loc + radius + .5);
428 context->line_to (girth-2.5, unity_loc + radius + .5);
432 if ( unity_loc < w ){
433 context->move_to (unity_loc - radius + .5, 3.5);
434 context->line_to (unity_loc - radius + .5, girth-3.5);
440 if (!darea.get_sensitive()) {
441 rounded_rectangle (context, 0, 0, darea.get_width(), darea.get_height(), 3);
442 context->set_source_rgba (0.505, 0.517, 0.525, 0.6);
444 } else if (_hovering) {
445 Gtkmm2ext::rounded_rectangle (cr, 1, 1, w-2, h-2, radius);
446 cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.1);
453 std::string const label = get_label (xpos);
454 if (!label.empty()) {
457 layout->set_text (label);
458 layout->get_pixel_size (twidth, theight);
461 cairo_set_source_rgba (cr, 0.17, 0.17, 0.17, 1.0);
462 context->move_to ( 5, (darea.get_height()/2) - (theight/2));
464 c = get_style()->get_text (get_state());
465 CairoWidget::set_source_rgb_a (cr, c, 0.7);
466 context->move_to ( w-twidth-5, (darea.get_height()/2) - (theight/2));
468 layout->show_in_cairo_context (context);
475 BarController::set_style (barStyle s)
482 BarController::switch_to_bar ()
490 if (get_child() == &darea) {
500 SpinnerActive (false); /* EMIT SIGNAL */
506 BarController::switch_to_spinner ()
514 if (get_child() == &spinner) {
521 spinner.select_region (0, spinner.get_text_length());
522 spinner.grab_focus ();
526 SpinnerActive (true); /* EMIT SIGNAL */
532 BarController::entry_activated ()
538 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
545 BarController::set_use_parent (bool yn)
552 BarController::set_sensitive (bool yn)
554 Alignment::set_sensitive (yn);
555 darea.set_sensitive (yn);
559 This is called when we need to update the adjustment with the value
560 from the spinner's text entry.
562 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
564 If we're not in logarithmic mode we can return false to use the
567 In theory we should check for conversion errors but set numeric
568 mode to true on the spinner prevents invalid input.
571 BarController::entry_input (double* new_value)
577 // extract a double from the string and take its log
578 Entry *entry = dynamic_cast<Entry *>(&spinner);
582 // Switch to user's preferred locale so that
583 // if they use different LC_NUMERIC conventions,
584 // we will honor them.
586 PBD::LocaleGuard lg ("");
587 sscanf (entry->get_text().c_str(), "%lf", &value);
590 *new_value = log(value);
596 This is called when we need to update the spinner's text entry
597 with the value of the adjustment.
599 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
601 If we're not in logarithmic mode we can return false to use the
605 BarController::entry_output ()
611 // generate the exponential and turn it into a string
612 // convert to correct locale.
620 // Switch to user's preferred locale so that
621 // if they use different LC_NUMERIC conventions,
622 // we will honor them.
624 PBD::LocaleGuard lg ("");
625 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
628 Entry *entry = dynamic_cast<Entry *>(&spinner);
629 entry->set_text(buf);