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, y1=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);
334 parent = get_parent();
337 c = parent->get_style()->get_fg (parent->get_state());
339 g = c.get_green_p ();
341 context->set_source_rgb (r, g, b);
342 context->rectangle (0, 0, darea.get_width(), darea.get_height());
348 c = get_style()->get_bg (get_state());
350 g = c.get_green_p ();
352 context->set_source_rgb (r, g, b);
353 context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
357 c = get_style()->get_fg (get_state());
359 g = c.get_green_p ();
361 context->set_source_rgb (r, g, b);
362 context->move_to (x1, 0);
363 context->line_to (x1, h);
368 w = darea.get_width() - 1;
369 h = darea.get_height();
370 x1 = (gint) floor (w * fract);
374 parent = get_parent();
377 c = parent->get_style()->get_fg (parent->get_state());
379 g = c.get_green_p ();
381 context->set_source_rgb (r, g, b);
382 context->rectangle (0, 0, darea.get_width(), darea.get_height());
388 c = get_style()->get_bg (get_state());
390 g = c.get_green_p ();
392 context->set_source_rgb (r, g, b);
393 context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
397 c = get_style()->get_fg (get_state());
399 g = c.get_green_p ();
401 context->arc (x1, ((h-2)/2)-1, x2, 0, 2*M_PI);
405 w = darea.get_width();
406 h = darea.get_height()-2;
408 parent = get_parent();
410 c = parent->get_style()->get_fg (parent->get_state());
412 g = c.get_green_p ();
414 context->set_source_rgb (r, g, b);
415 context->rectangle (0, 0, darea.get_width(), darea.get_height());
419 c = get_style()->get_bg (get_state());
421 g = c.get_green_p ();
423 context->set_source_rgb (r, g, b);
424 context->rectangle (0, 0, darea.get_width(), darea.get_height());
427 c = get_style()->get_fg (get_state());
429 g = c.get_green_p ();
431 x1 = (w/2) - ((w*fract)/2); // center, back up half the bar width
432 context->set_source_rgb (r, g, b);
433 context->rectangle (x1, 1, w*fract, h);
439 w = darea.get_width() - 2;
440 h = darea.get_height() - 2;
442 x2 = (gint) floor (w * fract);
449 context->set_source_rgb (0,0,0);
450 cairo_rectangle (context->cobj(), 0, 0, darea.get_width(), darea.get_height());
453 /* draw active box */
455 context->set_source (pattern);
456 rounded_rectangle (context, 1, 1, x2, y2, radius-1.5);
459 // context->set_source (shine_pattern);
460 // rounded_rectangle (context, 2, 3, x2-2, y2-8, radius-2);
475 std::string const label = get_label (xpos);
477 if (!label.empty()) {
479 layout->set_text (label);
481 int width, height, x;
482 layout->get_pixel_size (width, height);
485 x = max (3, 1 + (x2 - (width/2)));
486 x = min (darea.get_width() - width - 3, (int) lrint (xpos));
488 x = lrint (darea.get_width() * xpos);
491 c = get_style()->get_text (get_state());
493 g = c.get_green_p ();
495 context->set_source_rgb (r, g, b);
496 context->move_to (x, (darea.get_height()/2) - (height/2));
497 layout->show_in_cairo_context (context);
504 BarController::set_style (barStyle s)
511 BarController::switch_to_bar ()
519 if (get_child() == &darea) {
529 SpinnerActive (false); /* EMIT SIGNAL */
535 BarController::switch_to_spinner ()
543 if (get_child() == &spinner) {
550 spinner.select_region (0, spinner.get_text_length());
551 spinner.grab_focus ();
555 SpinnerActive (true); /* EMIT SIGNAL */
561 BarController::entry_activated ()
567 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
574 BarController::set_use_parent (bool yn)
581 BarController::set_sensitive (bool yn)
583 Frame::set_sensitive (yn);
584 darea.set_sensitive (yn);
588 This is called when we need to update the adjustment with the value
589 from the spinner's text entry.
591 We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
593 If we're not in logarithmic mode we can return false to use the
596 In theory we should check for conversion errors but set numeric
597 mode to true on the spinner prevents invalid input.
600 BarController::entry_input (double* new_value)
606 // extract a double from the string and take its log
607 Entry *entry = dynamic_cast<Entry *>(&spinner);
611 // Switch to user's preferred locale so that
612 // if they use different LC_NUMERIC conventions,
613 // we will honor them.
615 PBD::LocaleGuard lg ("");
616 sscanf (entry->get_text().c_str(), "%lf", &value);
619 *new_value = log(value);
625 This is called when we need to update the spinner's text entry
626 with the value of the adjustment.
628 We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
630 If we're not in logarithmic mode we can return false to use the
634 BarController::entry_output ()
640 // generate the exponential and turn it into a string
641 // convert to correct locale.
649 // Switch to user's preferred locale so that
650 // if they use different LC_NUMERIC conventions,
651 // we will honor them.
653 PBD::LocaleGuard lg ("");
654 snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
657 Entry *entry = dynamic_cast<Entry *>(&spinner);
658 entry->set_text(buf);