'libs/gtkmm2ext' - Remove "using namespace Gdk;" to avoid confusion (in 'fastmeter...
[ardour.git] / libs / gtkmm2ext / barcontroller.cc
index c3c15b4281bf47ff229da0b90f3b6f3be3a054cb..693151aaed120042eff57b1c1bbd6307b6f30170 100644 (file)
 */
 
 #include <string>
+#include <sstream>
 #include <climits>
 #include <cstdio>
 #include <cmath>
 #include <algorithm>
 
 #include <pbd/controllable.h>
+#include <pbd/locale_guard.h>
 
-#include <gtkmm2ext/gtk_ui.h>
-#include <gtkmm2ext/utils.h>
-#include <gtkmm2ext/barcontroller.h>
+#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/utils.h"
+#include "gtkmm2ext/keyboard.h"
+#include "gtkmm2ext/barcontroller.h"
 
 #include "i18n.h"
 
@@ -36,12 +39,10 @@ using namespace Gtk;
 using namespace Gtkmm2ext;
 
 BarController::BarController (Gtk::Adjustment& adj,
-                             PBD::Controllable& mc,
-                             sigc::slot<void,char*,unsigned int> lc) 
+                             boost::shared_ptr<PBD::Controllable> mc)
 
        : adjustment (adj),
          binding_proxy (mc),
-         label_callback (lc),
          spinner (adjustment)
 
 {                        
@@ -49,8 +50,8 @@ BarController::BarController (Gtk::Adjustment& adj,
        grabbed = false;
        switching = false;
        switch_on_release = false;
-       with_text = true;
        use_parent = false;
+       logarithmic = false;
 
        layout = darea.create_pango_layout("");
 
@@ -76,12 +77,22 @@ BarController::BarController (Gtk::Adjustment& adj,
 
        spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
        spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
-       spinner.set_digits (3);
-
+       spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
+       spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
+       spinner.set_digits (9);
+       spinner.set_numeric (true);
+       
        add (darea);
+
        show_all ();
 }
 
+BarController::~BarController ()
+{
+//     delete pattern;
+//     delete shine_pattern;
+}
+
 void
 BarController::drop_grab ()
 {
@@ -144,14 +155,14 @@ BarController::button_release (GdkEventButton* ev)
                        return true;
                }
 
-               if ((ev->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == GDK_SHIFT_MASK) {
+               if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
                        adjustment.set_value (initial_value);
                } else {
                        double scale;
 
-                       if (ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
+                       if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
                                scale = 0.01;
-                       } else if (ev->state & GDK_CONTROL_MASK) {
+                       } else if (ev->state & Keyboard::PrimaryModifier) {
                                scale = 0.1;
                        } else {
                                scale = 1.0;
@@ -179,9 +190,9 @@ BarController::scroll (GdkEventScroll* ev)
 {
        double scale;
 
-       if (ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
+       if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
                scale = 0.01;
-       } else if (ev->state & GDK_CONTROL_MASK) {
+       } else if (ev->state & Keyboard::PrimaryModifier) {
                scale = 0.1;
        } else {
                scale = 1.0;
@@ -211,13 +222,13 @@ BarController::motion (GdkEventMotion* ev)
                return true;
        }
 
-       if ((ev->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == GDK_SHIFT_MASK) {
+       if ((ev->state & (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier)) == Keyboard::TertiaryModifier) {
                return TRUE;
        }
 
-       if (ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
+       if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
                scale = 0.01;
-       } else if (ev->state & GDK_CONTROL_MASK) {
+       } else if (ev->state & Keyboard::PrimaryModifier) {
                scale = 0.1;
        } else {
                scale = 1.0;
@@ -240,16 +251,17 @@ BarController::mouse_control (double x, GdkWindow* window, double scaling)
 
        delta = x - grab_x;
        grab_x = x;
-
+        
        switch (_style) {
        case Line:
+       case Blob:
        case LeftToRight:
+        case CenterOut:
                fract = scaling * (delta / (darea.get_width() - 2));
                fract = min (1.0, fract);
                fract = max (-1.0, fract);
                adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
                break;
-
        default:
                fract = 0.0;
        }
@@ -258,14 +270,53 @@ BarController::mouse_control (double x, GdkWindow* window, double scaling)
        return TRUE;
 }
 
+void
+BarController::create_patterns ()
+{
+       Glib::RefPtr<Gdk::Window> win (darea.get_window());
+    Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
+
+       Gdk::Color c = get_style()->get_fg (get_state());
+    float r, g, b;
+       r = c.get_red_p ();
+       g = c.get_green_p ();
+       b = c.get_blue_p ();
+
+       float rheight = darea.get_height()-2;
+
+       cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, rheight);
+       cairo_pattern_add_color_stop_rgba (pat, 0, r*0.8,g*0.8,b*0.8, 1.0);
+       cairo_pattern_add_color_stop_rgba (pat, 1, r*0.6,g*0.6,b*0.6, 1.0);
+       Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
+       pattern = p;
+       cairo_pattern_destroy(pat);
+
+       pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, rheight);
+       cairo_pattern_add_color_stop_rgba (pat, 0, 1,1,1,0.0);
+       cairo_pattern_add_color_stop_rgba (pat, 0.2, 1,1,1,0.3);
+       cairo_pattern_add_color_stop_rgba (pat, 0.5, 1,1,1,0.0);
+       cairo_pattern_add_color_stop_rgba (pat, 1, 1,1,1,0.0);
+       Cairo::RefPtr<Cairo::Pattern> p2 (new Cairo::Pattern (pat, false));
+       shine_pattern = p2;
+       cairo_pattern_destroy(pat);
+
+}
+
 bool
-BarController::expose (GdkEventExpose* event)
+BarController::expose (GdkEventExpose* /*event*/)
 {
        Glib::RefPtr<Gdk::Window> win (darea.get_window());
+       Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
+
+       if( !pattern )
+               create_patterns();
+
+       Gdk::Color c;
        Widget* parent;
-       gint x1=0, x2=0, y1=0, y2=0;
+       gint x1=0, x2=0, y2=0;
        gint w, h;
-       double fract;
+       double fract, radius;
+    float r, g, b;
 
        fract = ((adjustment.get_value() - adjustment.get_lower()) /
                 (adjustment.get_upper() - adjustment.get_lower()));
@@ -276,29 +327,110 @@ BarController::expose (GdkEventExpose* event)
                h = darea.get_height();
                x1 = (gint) floor (w * fract);
                x2 = x1;
-               y1 = 0;
                y2 = h - 1;
 
+               if (use_parent) {
+                       parent = get_parent();
+                        
+                       if (parent) {
+                                c = parent->get_style()->get_fg (parent->get_state());
+                                r = c.get_red_p ();
+                                g = c.get_green_p ();
+                                b = c.get_blue_p ();
+                                context->set_source_rgb (r, g, b);
+                                context->rectangle (0, 0, darea.get_width(), darea.get_height());
+                                context->fill ();
+                       }
+
+               } else {
+
+                        c = get_style()->get_bg (get_state());
+                        r = c.get_red_p ();
+                        g = c.get_green_p ();
+                        b = c.get_blue_p ();
+                        context->set_source_rgb (r, g, b);
+                        context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
+                        context->fill ();
+               }
+                
+                c = get_style()->get_fg (get_state());
+                r = c.get_red_p ();
+                g = c.get_green_p ();
+                b = c.get_blue_p ();
+                context->set_source_rgb (r, g, b);
+                context->move_to (x1, 0);
+                context->line_to (x1, h);
+                context->stroke ();
+               break;
+
+        case Blob:
+               w = darea.get_width() - 1;
+               h = darea.get_height();
+               x1 = (gint) floor (w * fract);
+               x2 = min (w-2,h-2);
+
                if (use_parent) {
                        parent = get_parent();
                        
                        if (parent) {
-                               win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
-                                                    true,
-                                                    0, 0, darea.get_width(), darea.get_height());
+                                c = parent->get_style()->get_fg (parent->get_state());
+                                r = c.get_red_p ();
+                                g = c.get_green_p ();
+                                b = c.get_blue_p ();
+                                context->set_source_rgb (r, g, b);
+                                context->rectangle (0, 0, darea.get_width(), darea.get_height());
+                                context->fill ();
                        }
 
                } else {
 
-                       win->draw_rectangle (get_style()->get_bg_gc (get_state()),
-                                            true,
-                                            0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
+                        c = get_style()->get_bg (get_state());
+                        r = c.get_red_p ();
+                        g = c.get_green_p ();
+                        b = c.get_blue_p ();
+                        context->set_source_rgb (r, g, b);
+                        context->rectangle (0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
+                        context->fill ();
                }
                
-               win->draw_line (get_style()->get_fg_gc (get_state()), x1, 0, x1, h);
+                c = get_style()->get_fg (get_state());
+                r = c.get_red_p ();
+                g = c.get_green_p ();
+                b = c.get_blue_p ();
+                context->arc (x1, ((h-2)/2)-1, x2, 0, 2*M_PI);
                break;
 
        case CenterOut:
+               w = darea.get_width();
+               h = darea.get_height()-2;
+                if (use_parent) {
+                        parent = get_parent();
+                        if (parent) {
+                                c = parent->get_style()->get_fg (parent->get_state());
+                                r = c.get_red_p ();
+                                g = c.get_green_p ();
+                                b = c.get_blue_p ();
+                                context->set_source_rgb (r, g, b);
+                                context->rectangle (0, 0, darea.get_width(), darea.get_height());
+                                context->fill ();
+                        }
+                } else {
+                        c = get_style()->get_bg (get_state());
+                        r = c.get_red_p ();
+                        g = c.get_green_p ();
+                        b = c.get_blue_p ();
+                        context->set_source_rgb (r, g, b);
+                        context->rectangle (0, 0, darea.get_width(), darea.get_height());
+                        context->fill ();
+                }
+                c = get_style()->get_fg (get_state());
+                r = c.get_red_p ();
+                g = c.get_green_p ();
+                b = c.get_blue_p ();
+                x1 = (w/2) - ((w*fract)/2); // center, back up half the bar width
+                context->set_source_rgb (r, g, b);
+                context->rectangle (x1, 1, w*fract, h);
+                context->fill ();
                break;
 
        case LeftToRight:
@@ -306,33 +438,26 @@ BarController::expose (GdkEventExpose* event)
                w = darea.get_width() - 2;
                h = darea.get_height() - 2;
 
-               x1 = 0;
                x2 = (gint) floor (w * fract);
-               y1 = 0;
-               y2 = h - 1;
+               y2 = h;
+               radius = 4;
+               if (x2 < 8) x2 = 8;
 
-               win->draw_rectangle (get_style()->get_bg_gc (get_state()),
-                                   false,
-                                   0, 0, darea.get_width() - 1, darea.get_height() - 1);
+               /* border */
 
-               /* draw active box */
+               context->set_source_rgb (0,0,0);
+               cairo_rectangle (context->cobj(), 0, 0, darea.get_width(), darea.get_height());
+               context->fill ();
 
-               win->draw_rectangle (get_style()->get_fg_gc (get_state()),
-                                   true,
-                                   1 + x1,
-                                   1 + y1,
-                                   x2,
-                                   1 + y2);
-               
-               /* draw inactive box */
+               /* draw active box */
 
-               win->draw_rectangle (get_style()->get_fg_gc (STATE_INSENSITIVE),
-                                   true,
-                                   1 + x2,
-                                   1 + y1,
-                                   w - x2,
-                                   1 + y2);
+               context->set_source (pattern);
+               rounded_rectangle (context, 1, 1, x2, y2, radius-1.5);
+               context->fill ();
 
+//             context->set_source (shine_pattern);
+//             rounded_rectangle (context, 2, 3, x2-2, y2-8, radius-2);
+//             context->fill ();
                break;
 
        case RightToLeft:
@@ -343,47 +468,45 @@ BarController::expose (GdkEventExpose* event)
                break;
        }
 
-       if (with_text) {
-               /* draw label */
-               
-               char buf[64];
-               buf[0] = '\0';
-
-               label_callback (buf, 64);
-
-               if (buf[0] != '\0') {
+       if (!darea.get_sensitive()) {
+               rounded_rectangle (context, 0, 0, darea.get_width(), darea.get_height(), 3);
+               context->set_source_rgba (0.505, 0.517, 0.525, 0.6);
+               context->fill ();
+       }
 
-                       layout->set_text (buf);                 
+       /* draw label */
 
-                       int width, height;
-                       layout->get_pixel_size (width, height);
+       double xpos = -1;
+       std::string const label = get_label (xpos);
 
-                       int xpos;
+       if (!label.empty()) {
+               
+               layout->set_text (label);
+               
+               int width, height, x;
+               layout->get_pixel_size (width, height);
 
-                       xpos = max (3, 1 + (x2 - (width/2)));
-                       xpos = min (darea.get_width() - width - 3, xpos);
-                       
-                       win->draw_layout (get_style()->get_text_gc (get_state()),
-                                         xpos,
-                                         (darea.get_height()/2) - (height/2),
-                                         layout);
-               }
+               if (xpos == -1) {
+                       x = max (3, 1 + (x2 - (width/2)));
+                       x = min (darea.get_width() - width - 3, (int) lrint (xpos));
+               } else {
+                        x = lrint (darea.get_width() * xpos);
+                }
+
+                c = get_style()->get_text (get_state());
+                r = c.get_red_p ();
+                g = c.get_green_p ();
+                b = c.get_blue_p ();
+                context->set_source_rgb (r, g, b);
+                context->move_to (x, (darea.get_height()/2) - (height/2));
+                layout->show_in_cairo_context (context);
        }
-
+       
        return true;
 }
 
 void
-BarController::set_with_text (bool yn)
-{
-       if (with_text != yn) {
-               with_text = yn;
-               queue_draw ();
-       }
-}
-
-void
-BarController::set_style (Style s)
+BarController::set_style (barStyle s)
 {
        _style = s;
        darea.queue_draw ();
@@ -407,6 +530,9 @@ BarController::switch_to_bar ()
        darea.show ();
 
        switching = false;
+
+       SpinnerActive (false); /* EMIT SIGNAL */
+       
        return FALSE;
 }
 
@@ -430,24 +556,20 @@ BarController::switch_to_spinner ()
        spinner.grab_focus ();
 
        switching = false;
+
+       SpinnerActive (true); /* EMIT SIGNAL */
+
        return FALSE;
 }
 
 void
 BarController::entry_activated ()
 {
-       string text = spinner.get_text ();
-       float val;
-
-       if (sscanf (text.c_str(), "%f", &val) == 1) {
-               adjustment.set_value (val);
-       }
-       
        switch_to_bar ();
 }
 
 bool
-BarController::entry_focus_out (GdkEventFocus* ev)
+BarController::entry_focus_out (GdkEventFocus* /*ev*/)
 {
        entry_activated ();
        return true;
@@ -466,3 +588,82 @@ BarController::set_sensitive (bool yn)
        Frame::set_sensitive (yn);
        darea.set_sensitive (yn);
 }
+
+/* 
+    This is called when we need to update the adjustment with the value
+    from the spinner's text entry.
+    
+    We need to use Gtk::Entry::get_text to avoid recursive nastiness :)
+    
+    If we're not in logarithmic mode we can return false to use the 
+    default conversion.
+    
+    In theory we should check for conversion errors but set numeric
+    mode to true on the spinner prevents invalid input.
+*/
+int
+BarController::entry_input (double* new_value)
+{
+       if (!logarithmic) {
+               return false;
+       }
+
+       // extract a double from the string and take its log
+       Entry *entry = dynamic_cast<Entry *>(&spinner);
+       double value;
+
+       {
+               // Switch to user's preferred locale so that
+               // if they use different LC_NUMERIC conventions,
+               // we will honor them.
+
+               PBD::LocaleGuard lg ("");
+               sscanf (entry->get_text().c_str(), "%lf", &value);
+       }
+
+       *new_value = log(value);
+
+       return true;
+}
+
+/* 
+    This is called when we need to update the spinner's text entry 
+    with the value of the adjustment.
+    
+    We need to use Gtk::Entry::set_text to avoid recursive nastiness :)
+    
+    If we're not in logarithmic mode we can return false to use the 
+    default conversion.
+*/
+bool
+BarController::entry_output ()
+{
+       if (!logarithmic) {
+               return false;
+       }
+
+       // generate the exponential and turn it into a string
+       // convert to correct locale. 
+       
+       stringstream stream;
+       string str;
+
+       char buf[128];
+
+       {
+               // Switch to user's preferred locale so that
+               // if they use different LC_NUMERIC conventions,
+               // we will honor them.
+               
+               PBD::LocaleGuard lg ("");
+               snprintf (buf, sizeof (buf), "%g", exp (spinner.get_adjustment()->get_value()));
+       }
+
+       Entry *entry = dynamic_cast<Entry *>(&spinner);
+       entry->set_text(buf);
+       
+       return true;
+}
+
+
+