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);
474 std::string const label = get_label (xpos);
476 if (!label.empty()) {
478 layout->set_text (label);
480 int width, height, x;
481 layout->get_pixel_size (width, height);
484 x = max (3, 1 + (x2 - (width/2)));
485 x = min (darea.get_width() - width - 3, (int) lrint (xpos));
487 x = lrint (darea.get_width() * xpos);
490 c = get_style()->get_text (get_state());
492 g = c.get_green_p ();
494 context->set_source_rgb (r, g, b);
495 context->move_to (x, (darea.get_height()/2) - (height/2));
496 layout->show_in_cairo_context (context);
503 BarController::set_style (barStyle s)
510 BarController::switch_to_bar ()
518 if (get_child() == &darea) {
528 SpinnerActive (false); /* EMIT SIGNAL */
534 BarController::switch_to_spinner ()
542 if (get_child() == &spinner) {
549 spinner.select_region (0, spinner.get_text_length());
550 spinner.grab_focus ();
554 SpinnerActive (true); /* EMIT SIGNAL */
560 BarController::entry_activated ()
566 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
573 BarController::set_use_parent (bool yn)
580 BarController::set_sensitive (bool yn)
582 Frame::set_sensitive (yn);
583 darea.set_sensitive (yn);
587 This is called when we need to update the adjustment with the value
588 from the spinner's text entry.
590 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
592 If we're not in logarithmic mode we can return false to use the
595 In theory we should check for conversion errors but set numeric
596 mode to true on the spinner prevents invalid input.
599 BarController::entry_input (double* new_value)
605 // extract a double from the string and take its log
606 Entry *entry = dynamic_cast<Entry *>(&spinner);
610 // Switch to user's preferred locale so that
611 // if they use different LC_NUMERIC conventions,
612 // we will honor them.
614 PBD::LocaleGuard lg ("");
615 sscanf (entry->get_text().c_str(), "%lf", &value);
618 *new_value = log(value);
624 This is called when we need to update the spinner's text entry
625 with the value of the adjustment.
627 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
629 If we're not in logarithmic mode we can return false to use the
633 BarController::entry_output ()
639 // generate the exponential and turn it into a string
640 // convert to correct locale.
648 // Switch to user's preferred locale so that
649 // if they use different LC_NUMERIC conventions,
650 // we will honor them.
652 PBD::LocaleGuard lg ("");
653 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
656 Entry *entry = dynamic_cast<Entry *>(&spinner);
657 entry->set_text(buf);