add icons for cut mouse mode button and cut cursor (both need improving)
[ardour.git] / gtk2_ardour / ardour_button.cc
index 89e6a20385a90a01b2e7ac337f11bd3686ab62fa..2525d57c07cf9d8613afd468eb183f5d23f2c437 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "pbd/compose.h"
 #include "pbd/error.h"
+#include "pbd/stacktrace.h"
 
 #include "gtkmm2ext/utils.h"
 #include "gtkmm2ext/rgb_macros.h"
@@ -64,9 +65,16 @@ ArdourButton::ArdourButton (Element e)
        , _angle(0)
        , _xalign(.5)
        , _yalign(.5)
+       , bg_color (0)
        , border_color (0)
-       , fill_color_active (0)
-       , fill_color_inactive (0)
+       , fill_start_inactive_color (0)
+       , fill_end_inactive_color (0)
+       , fill_start_active_color (0)
+       , fill_end_active_color (0)
+       , text_active_color(0)
+       , text_inactive_color(0)
+       , led_active_color(0)
+       , led_inactive_color(0)
        , fill_pattern (0)
        , fill_pattern_active (0)
        , shine_pattern (0)
@@ -78,8 +86,10 @@ ArdourButton::ArdourButton (Element e)
        , _fixed_diameter (true)
        , _distinct_led_click (false)
        , _hovering (false)
+       , _focused (false)
+       , _fixed_colors_set (false)
 {
-       ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
+       ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
 }
 
 ArdourButton::ArdourButton (const std::string& str, Element e)
@@ -93,9 +103,16 @@ ArdourButton::ArdourButton (const std::string& str, Element e)
        , _angle(0)
        , _xalign(.5)
        , _yalign(.5)
+       , bg_color (0)
        , border_color (0)
-       , fill_color_active (0)
-       , fill_color_inactive (0)
+       , fill_start_inactive_color (0)
+       , fill_end_inactive_color (0)
+       , fill_start_active_color (0)
+       , fill_end_active_color (0)
+       , text_active_color(0)
+       , text_inactive_color(0)
+       , led_active_color(0)
+       , led_inactive_color(0)
        , fill_pattern (0)
        , fill_pattern_active (0)
        , shine_pattern (0)
@@ -107,6 +124,8 @@ ArdourButton::ArdourButton (const std::string& str, Element e)
        , _fixed_diameter (true)
        , _distinct_led_click (false)
        , _hovering (false)
+       , _focused (false)
+       , _fixed_colors_set (false)
 {
        set_text (str);
 }
@@ -162,7 +181,7 @@ ArdourButton::set_markup (const std::string& str)
                _layout = Pango::Layout::create (get_pango_context());
        } 
 
-       _layout->set_text (str);
+       _layout->set_markup (str);
        queue_resize ();
 }
 
@@ -180,7 +199,7 @@ ArdourButton::set_alignment (const float xa, const float ya)
 }
 
 void
-ArdourButton::render (cairo_t* cr)
+ArdourButton::render (cairo_t* cr, cairo_rectangle_t *)
 {
        void (*rounded_function)(cairo_t*, double, double, double, double, double);
 
@@ -230,7 +249,7 @@ ArdourButton::render (cairo_t* cr)
                        
                        if (!(_tweaks & ImplicitUsesSolidColor)) {
                                //border
-                               UINT_TO_RGBA (fill_color_active, &r, &g, &b, &a);
+                               UINT_TO_RGBA (fill_end_active_color, &r, &g, &b, &a);
                                cairo_set_line_width (cr, 1.0);
                                rounded_function (cr, 2, 2, get_width()-4, get_height()-4, _corner_radius - 1.5);
                                cairo_set_source_rgba (cr, r/255.0, g/255.0, b/255.0, a/255.0);
@@ -252,7 +271,7 @@ ArdourButton::render (cairo_t* cr)
                }
        }
 
-       if ( ((_elements & FlatFace)==FlatFace) && (active_state() != Gtkmm2ext::ExplicitActive) ) {
+       if ( ((_elements & Inset)==Inset) && (active_state() != Gtkmm2ext::ExplicitActive) ) {
 
                if ( !_flat_buttons ) {
                        float rheight = get_height()*0.5-REFLECTION_HEIGHT;
@@ -263,7 +282,7 @@ ArdourButton::render (cairo_t* cr)
 
                if (active_state() == Gtkmm2ext::ExplicitActive) {
 
-                       UINT_TO_RGBA (fill_color_active, &r, &g, &b, &a);
+                       UINT_TO_RGBA (fill_start_active_color, &r, &g, &b, &a);
                        cairo_set_line_width (cr, 2.0);
                        rounded_function (cr, 2, 2, get_width()-4, get_height()-4, _corner_radius - 2.0);
                        cairo_set_source_rgba (cr, r/255.0, g/255.0, b/255.0, a/255.0);
@@ -271,7 +290,7 @@ ArdourButton::render (cairo_t* cr)
 
                } else {
 
-                       UINT_TO_RGBA (fill_color_inactive, &r, &g, &b, &a);
+                       UINT_TO_RGBA (fill_start_inactive_color, &r, &g, &b, &a);
                        cairo_set_line_width (cr, 2.0);
                        rounded_function (cr, 2, 2, get_width()-4, get_height()-4, _corner_radius - 2.0);
                        cairo_set_source_rgba (cr, r/255.0, g/255.0, b/255.0, a/255.0);
@@ -302,11 +321,17 @@ ArdourButton::render (cairo_t* cr)
        }
 
        if ( ((_elements & Text)==Text) && !_text.empty()) {
+               cairo_save (cr);
+               cairo_rectangle (cr, 2, 1, get_width()-4, get_height()-2);
+               cairo_clip(cr);
 
                cairo_new_path (cr);    
                cairo_set_source_rgba (cr, text_r, text_g, text_b, text_a);
 
-               if (_elements & Indicator) {
+               if ( (_elements & Menu) == Menu) {
+                       cairo_move_to (cr, text_margin, get_height()/2.0 - _text_height/2.0);
+                       pango_cairo_show_layout (cr, _layout->gobj());
+               } else if ( (_elements & Indicator)  == Indicator) {
                        if (_led_left) {
                                cairo_move_to (cr, text_margin + _diameter + 4, get_height()/2.0 - _text_height/2.0);
                        } else {
@@ -342,9 +367,26 @@ ArdourButton::render (cairo_t* cr)
                        /* use old center'ed layout for follow up items - until rotation/aligment code is completed */
                        cairo_move_to (cr, (get_width() - _text_width)/2.0, get_height()/2.0 - _text_height/2.0);
                }
-
+               cairo_restore (cr);
        } 
 
+       if (((_elements & Menu)==Menu)) {
+       
+               cairo_save (cr);
+
+               cairo_translate (cr, 0,0 );
+               
+               //white arrow
+               cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
+               cairo_move_to(cr, get_width() - ((_diameter/2.0) + 6.0), get_height()/2.0 +_diameter/4);
+               cairo_rel_line_to(cr, -_diameter/2, -_diameter/2);
+               cairo_rel_line_to(cr, _diameter, 0);
+               cairo_close_path(cr);
+               cairo_fill(cr);
+                       
+               cairo_restore (cr);
+       }
+       
        if (((_elements & Indicator)==Indicator)) {
 
                /* move to the center of the indicator/led */
@@ -409,6 +451,16 @@ ArdourButton::render (cairo_t* cr)
                        cairo_fill (cr);
                }
        }
+       if (_focused) {
+               rounded_function (cr, 1.5, 1.5, get_width() - 3, get_height() - 3, _corner_radius);
+               cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.8);
+               double dashes = 1;
+               cairo_set_dash (cr, &dashes, 1, 0);
+               cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+               cairo_set_line_width (cr, 1.0);
+               cairo_stroke (cr);
+               cairo_set_dash (cr, 0, 0, 0);
+       }
 }
 
 void
@@ -420,7 +472,7 @@ ArdourButton::set_diameter (float d)
                _fixed_diameter = true;
        }
 
-       set_colors ();
+       build_patterns ();
        queue_resize ();
 }
 
@@ -472,24 +524,78 @@ ArdourButton::on_size_request (Gtk::Requisition* req)
                        req->height = _text_height + ypad;
                }
        }
+       req->width += _corner_radius;
 }
 
+/**
+ * This sets the colors used for rendering based on the name of the button, and
+ * thus uses information from the GUI config data. 
+ */
 void
 ArdourButton::set_colors ()
+{
+       if (_fixed_colors_set) {
+               return;
+       }
+       std::string name = get_name();
+
+       border_color = ARDOUR_UI::config()->color_by_name ("button border");
+
+       fill_start_active_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start active", name));
+       fill_end_active_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", name));
+       
+       fill_start_inactive_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start", name));
+        fill_end_inactive_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end", name));
+
+       text_active_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text active", name));
+       text_inactive_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", name));
+
+       led_active_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", name));
+       led_inactive_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led", name));
+}
+/**
+ * This sets the colors used for rendering based on two fixed values, rather
+ * than basing them on the button name, and thus information in the GUI config
+ * data.
+ */
+void ArdourButton::set_fixed_colors (const uint32_t color_active, const uint32_t color_inactive)
+{
+       _fixed_colors_set = true;
+
+       fill_start_active_color = fill_end_active_color = color_active;
+
+       unsigned char r, g, b, a;
+       UINT_TO_RGBA(color_active, &r, &g, &b, &a);
+       
+       double white_contrast = (max (double(r), 255.) - min (double(r), 255.)) +
+               (max (double(g), 255.) - min (double(g), 255.)) +
+               (max (double(b), 255.) - min (double(b), 255.));
+       
+       double black_contrast = (max (double(r), 0.) - min (double(r), 0.)) +
+               (max (double(g), 0.) - min (double(g), 0.)) +
+               (max (double(b), 0.) - min (double(b), 0.));
+       
+       text_active_color =
+               text_inactive_color = (white_contrast > black_contrast) ?
+               RGBA_TO_UINT(255, 255, 255, 255) : /* use white */
+               RGBA_TO_UINT(  0,   0,   0,   255);  /* use black */
+       
+       fill_start_inactive_color = fill_end_inactive_color = color_inactive;
+
+       /* XXX what about led colors ? */
+
+       build_patterns ();
+       set_name (""); /* this will trigger a "style-changed" message and then set_colors() */
+}
+
+void
+ArdourButton::build_patterns ()
 {
        uint32_t start_color;
        uint32_t end_color;
-       uint32_t r, g, b, a;
        uint32_t text_color;
        uint32_t led_color;
-
-       if (active_state() == Gtkmm2ext::ImplicitActive && (_tweaks & ImplicitUsesSolidColor)) {
-               fill_color_active = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
-       } else {
-               fill_color_active = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", get_name()));
-       }
-       fill_color_inactive = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end", get_name()));
-       border_color = ARDOUR_UI::config()->color_by_name ( "button border" );
+       uint32_t r, g, b, a;
 
        if (shine_pattern) {
                cairo_pattern_destroy (shine_pattern);
@@ -508,12 +614,11 @@ ArdourButton::set_colors ()
 
        if (_elements & Body) {
 
-               start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start active", get_name()));
-               
                if (_flat_buttons) {
-                       end_color = start_color;
+                       end_color = start_color = fill_start_active_color;
                } else {
-                       end_color = fill_color_active;
+                       start_color = fill_start_active_color;
+                       end_color = fill_end_active_color;
                }
                UINT_TO_RGBA (start_color, &r, &g, &b, &a);
 
@@ -530,10 +635,10 @@ ArdourButton::set_colors ()
 
                fill_pattern = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height()-3);
                if (_flat_buttons) {
-                       end_color = start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end", get_name()));
+                       end_color = start_color = fill_start_inactive_color;
                } else {
-                       start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start", get_name()));
-                       end_color = fill_color_inactive;
+                       start_color = fill_start_inactive_color;
+                       end_color = fill_end_inactive_color;
                }
                UINT_TO_RGBA (start_color, &r, &g, &b, &a);
                cairo_pattern_add_color_stop_rgba (fill_pattern, 0, r/255.0,g/255.0,b/255.0, a/255.0);
@@ -543,17 +648,16 @@ ArdourButton::set_colors ()
                fill_pattern_active = cairo_pattern_create_linear (0.0, 0.0, 0.0, get_height()-3);
                if (_flat_buttons) {
                        if (active_state() == Gtkmm2ext::ImplicitActive && (_tweaks & ImplicitUsesSolidColor)) {
-                               end_color = start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
+                               end_color = start_color = led_active_color;
                        } else {
-                               end_color = start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", get_name()));
+                               end_color = start_color = fill_end_active_color;
                        }
                } else {
                        if (active_state() == Gtkmm2ext::ImplicitActive && (_tweaks & ImplicitUsesSolidColor)) {
-                               start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led", get_name()));
-                               end_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
+                               end_color = start_color = led_active_color;
                        } else {
-                               start_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill start active", get_name()));
-                               end_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: fill end active", get_name()));
+                               start_color = fill_start_active_color;
+                               end_color = fill_end_active_color;
                        }
                }
                UINT_TO_RGBA (start_color, &r, &g, &b, &a);
@@ -579,17 +683,15 @@ ArdourButton::set_colors ()
                cairo_pattern_add_color_stop_rgba (reflection_pattern, 0, 1,1,1, active_state() ? 0.4 : 0.2);
                cairo_pattern_add_color_stop_rgba (reflection_pattern, 1, 1,1,1, 0.0);
        }
-       
-       /* text and LED colors */
 
        if (active_state() == Gtkmm2ext::ExplicitActive || ((_tweaks & ImplicitUsesSolidColor) && active_state() == Gtkmm2ext::ImplicitActive)) {
-               text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text active", get_name()));
-               led_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led active", get_name()));
+               text_color = text_active_color;
+               led_color = led_active_color;
        } else {
-               text_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: text", get_name()));
-               led_color = ARDOUR_UI::config()->color_by_name (string_compose ("%1: led", get_name()));
+               text_color = text_inactive_color;
+               led_color = led_inactive_color;
        }
-
+       
        UINT_TO_RGBA (text_color, &r, &g, &b, &a);
        text_r = r/255.0;
        text_g = g/255.0;
@@ -641,7 +743,7 @@ ArdourButton::on_button_press_event (GdkEventButton *ev)
 bool
 ArdourButton::on_button_release_event (GdkEventButton *ev)
 {
-       if ((_elements & Indicator) && _led_rect && _distinct_led_click) {
+       if (_hovering && (_elements & Indicator) && _led_rect && _distinct_led_click) {
                if (ev->x >= _led_rect->x && ev->x < _led_rect->x + _led_rect->width && 
                    ev->y >= _led_rect->y && ev->y < _led_rect->y + _led_rect->height) {
                        signal_led_clicked(); /* EMIT SIGNAL */
@@ -653,16 +755,17 @@ ArdourButton::on_button_release_event (GdkEventButton *ev)
                unset_active_state ();
        }
 
-       signal_clicked ();
-
-       if (_act_on_release) {
-               if (_action) {
-                       _action->activate ();
-                       return true;
+       if (_hovering) {
+               signal_clicked ();
+               
+               if (_act_on_release) {
+                       if (_action) {
+                               _action->activate ();
+                               return true;
+                       }
                }
        }
 
-
        return false;
 }
 
@@ -677,6 +780,7 @@ void
 ArdourButton::color_handler ()
 {
        set_colors ();
+       build_patterns ();
        set_dirty ();
 }
 
@@ -685,7 +789,7 @@ ArdourButton::on_size_allocate (Allocation& alloc)
 {
        CairoWidget::on_size_allocate (alloc);
        setup_led_rect ();
-       set_colors ();
+       build_patterns ();
 }
 
 void
@@ -759,6 +863,14 @@ void
 ArdourButton::on_style_changed (const RefPtr<Gtk::Style>&)
 {
        set_colors ();
+       build_patterns ();
+}
+
+void
+ArdourButton::on_name_changed ()
+{
+       set_colors ();
+       build_patterns ();
 }
 
 void
@@ -810,6 +922,7 @@ ArdourButton::set_active_state (Gtkmm2ext::ActiveState s)
        CairoWidget::set_active_state (s);
        if (changed) {
                set_colors ();
+               build_patterns ();
        }
 }
        
@@ -820,9 +933,41 @@ ArdourButton::set_visual_state (Gtkmm2ext::VisualState s)
        CairoWidget::set_visual_state (s);
        if (changed) {
                set_colors ();
+               build_patterns ();
        }
 }
        
+
+bool
+ArdourButton::on_focus_in_event (GdkEventFocus* ev)
+{
+       _focused = true;
+       queue_draw ();
+       return CairoWidget::on_focus_in_event (ev);
+}
+
+bool
+ArdourButton::on_focus_out_event (GdkEventFocus* ev)
+{
+       _focused = false;
+       queue_draw ();
+       return CairoWidget::on_focus_out_event (ev);
+}
+
+bool
+ArdourButton::on_key_release_event (GdkEventKey *ev) {
+       if (_focused &&
+                       (ev->keyval == GDK_KEY_space || ev->keyval == GDK_Return))
+       {
+               signal_clicked();
+               if (_action) {
+                       _action->activate ();
+               }
+               return true;
+       }
+       return CairoWidget::on_key_release_event (ev);
+}
+
 bool
 ArdourButton::on_enter_notify_event (GdkEventCrossing* ev)
 {
@@ -896,14 +1041,12 @@ void
 ArdourButton::set_elements (Element e)
 {
        _elements = e;
-       set_colors ();
 }
 
 void
 ArdourButton::add_elements (Element e)
 {
        _elements = (ArdourButton::Element) (_elements | e);
-       set_colors ();
 }
 
 void