MIDI/Controllables for monitor section, and related fixes
[ardour.git] / gtk2_ardour / utils.cc
index df922c2d629239b380feb5c585fd9c012a5b070f..aa57a3b773cb87a60f3ab79fd6a9542958477e93 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2003 Paul Davis 
+    Copyright (C) 2003 Paul Davis
 
     This program is free software; you an redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <gtkmm2ext/utils.h>
 #include "ardour/configuration.h"
-#include "ardour/configuration.h"
+#include "ardour/rc_configuration.h"
 
 #include "ardour/filesystem_paths.h"
 
 #include "ardour_ui.h"
+#include "public_editor.h"
 #include "keyboard.h"
 #include "utils.h"
 #include "i18n.h"
 #include "rgb_macros.h"
 #include "canvas_impl.h"
+#include "gui_thread.h"
 
 using namespace std;
 using namespace Gtk;
-using namespace sigc;
 using namespace Glib;
 using namespace PBD;
+using Gtkmm2ext::Keyboard;
 
 sigc::signal<void>  DPIReset;
 
@@ -98,7 +100,7 @@ fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font
                        actual_width = width;
                        break;
                }
-               
+
                ustr.erase (last--);
                shorter_by++;
 
@@ -127,7 +129,7 @@ fit_to_pixels (cairo_t* cr, std::string name, double avail)
 
        bool abbreviated = false;
        uint32_t width = 0;
-               
+
        while (1) {
                cairo_text_extents_t ext;
                cairo_text_extents (cr, name.c_str(), &ext);
@@ -186,9 +188,9 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
        uint32_t t, x, y, colors, cpp;
        unsigned char c;
        unsigned char *savergb, *rgb;
-       
+
        // PARSE HEADER
-       
+
        if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
                error << string_compose (_("bad XPM header %1"), xpm[0])
                      << endmsg;
@@ -196,13 +198,13 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
        }
 
        savergb = rgb = (unsigned char*) malloc (h * w * 3);
-       
+
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
        for (t = 0; t < colors; ++t) {
                sscanf (xpm[t+1], "%c c #%lx", &c, &val);
                vals[c] = val;
        }
-       
+
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
@@ -239,7 +241,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
        }
 
        savergb = rgb = (unsigned char*) malloc (h * w * 4);
-       
+
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
 
        if (strstr (xpm[1], "None")) {
@@ -254,7 +256,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
                sscanf (xpm[t+1], "%c c #%lx", &c, &val);
                vals[c] = val;
        }
-       
+
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
@@ -310,17 +312,17 @@ get_font_for_style (string widgetname)
        style = foobar.get_style ();
 
        Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
-       
+
        PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
-       
+
        if (!pfd) {
-               
+
                /* layout inherited its font description from a PangoContext */
 
                PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
                pfd =  pango_context_get_font_description (ctxt);
                return new Pango::FontDescription (pfd, true); /* make a copy */
-       } 
+       }
 
        return new Pango::FontDescription (pfd, true); /* make a copy */
 }
@@ -339,12 +341,12 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
        }
 
        Gtk::Label foo;
-       
+
        window->add (foo);
 
        foo.set_name (style);
        foo.ensure_style ();
-       
+
        GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
 
        if (waverc) {
@@ -352,7 +354,7 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
                        r = waverc->fg[state].red / 257;
                        g = waverc->fg[state].green / 257;
                        b = waverc->fg[state].blue / 257;
+
                        /* what a hack ... "a" is for "active" */
                        if (state == Gtk::STATE_NORMAL && rgba) {
                                a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
@@ -376,7 +378,7 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
        }
 
        window->remove ();
-       
+
        if (state == Gtk::STATE_NORMAL && rgba) {
                return (uint32_t) RGBA_TO_UINT(r,g,b,a);
        } else {
@@ -490,7 +492,7 @@ gc_from_style (string widget_style_name, int state, string attr)
 }
 
 
-bool 
+bool
 canvas_item_visible (ArdourCanvas::Item* item)
 {
        return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
@@ -508,6 +510,22 @@ extern "C" {
 }
 #endif
 
+bool
+relay_key_press (GdkEventKey* ev, Gtk::Window* win)
+{
+       if (!key_press_focus_accelerator_handler (*win, ev)) {
+               return PublicEditor::instance().on_key_press_event(ev);
+       } else {
+               return true;
+       }
+}
+
+bool
+forward_key_press (GdkEventKey* ev)
+{
+        return PublicEditor::instance().on_key_press_event(ev);
+}
+
 bool
 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 {
@@ -524,8 +542,8 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
        if (focus) {
                if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
                        special_handling_of_unmodified_accelerators = true;
-               } 
-       } 
+               }
+       }
 
 #ifdef GTKOSX
        /* should this be universally true? */
@@ -536,7 +554,7 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 
 #ifdef DEBUG_ACCELERATOR_HANDLING
        if (debug) {
-               cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? " 
+               cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
                     << special_handling_of_unmodified_accelerators
                     << " magic widget focus ? "
                     << Keyboard::some_magic_widget_has_focus()
@@ -555,18 +573,18 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
               the focus widget and/or focus chain
 
           The problem with this is that if the accelerators include
-          keys without modifiers, such as the space bar or the 
+          keys without modifiers, such as the space bar or the
           letter "e", then pressing the key while typing into
           a text entry widget results in the accelerator being
           activated, instead of the desired letter appearing
           in the text entry.
 
           There is no good way of fixing this, but this
-          represents a compromise. The idea is that 
+          represents a compromise. The idea is that
           key events involving modifiers (not Shift)
           get routed into the activation pathway first, then
           get propagated to the focus widget if necessary.
-          
+
           If the key event doesn't involve modifiers,
           we deliver to the focus widget first, thus allowing
           it to get "normal text" without interference
@@ -637,9 +655,9 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 #endif
                return gtk_window_propagate_key_event (win, ev);
        }
-       
+
        /* no modifiers, propagate first */
-       
+
 #ifdef DEBUG_ACCELERATOR_HANDLING
        if (debug) {
                cerr << "\tpropagate, then activate\n";
@@ -653,15 +671,15 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 #endif
 
                if (allow_activating) {
-                       
+
 #ifdef GTKOSX
                        if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
                                return true;
                        }
 #endif
                        return gtk_window_activate_key (win, ev);
-               } 
-                       
+               }
+
        } else {
 #ifdef DEBUG_ACCELERATOR_HANDLING
                if (debug) {
@@ -679,22 +697,22 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
        return true;
 }
 
-Glib::RefPtr<Gdk::Pixbuf>      
+Glib::RefPtr<Gdk::Pixbuf>
 get_xpm (std::string name)
 {
        if (!xpm_map[name]) {
 
                SearchPath spath(ARDOUR::ardour_search_path());
                spath += ARDOUR::system_data_search_path();
-               
+
                spath.add_subdirectory_to_paths("pixmaps");
-               
+
                sys::path data_file_path;
-               
+
                if(!find_file_in_search_path (spath, name, data_file_path)) {
                        fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
                }
-               
+
                try {
                        xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path.to_string());
                } catch(const Glib::Error& e)   {
@@ -705,9 +723,8 @@ get_xpm (std::string name)
        return xpm_map[name];
 }
 
-
-Glib::RefPtr<Gdk::Pixbuf>      
-get_icon (const char* cname)
+Glib::ustring
+get_icon_path (const char* cname)
 {
        string name = cname;
        name += X_(".png");
@@ -723,9 +740,15 @@ get_icon (const char* cname)
                fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
        }
 
+       return data_file_path.to_string();
+}
+
+Glib::RefPtr<Gdk::Pixbuf>
+get_icon (const char* cname)
+{
        Glib::RefPtr<Gdk::Pixbuf> img;
        try {
-               img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
+               img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
        } catch (const Gdk::PixbufError &e) {
                cerr << "Caught PixbufError: " << e.what() << endl;
        } catch (...) {
@@ -744,22 +767,22 @@ longest (vector<string>& strings)
 
        vector<string>::iterator longest = strings.begin();
        string::size_type longest_length = (*longest).length();
-       
+
        vector<string>::iterator i = longest;
        ++i;
 
        while (i != strings.end()) {
-               
+
                string::size_type len = (*i).length();
-               
+
                if (len > longest_length) {
                        longest = i;
                        longest_length = len;
-               } 
-               
+               }
+
                ++i;
        }
-       
+
        return *longest;
 }
 
@@ -803,7 +826,7 @@ key_is_legal_for_numeric_entry (guint keyval)
        case GDK_Left:
        case GDK_Right:
                return true;
-               
+
        default:
                break;
        }
@@ -820,7 +843,7 @@ set_pango_fontsize ()
        pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
 
        /* Cairo rendering, in case there is any */
-       
+
        pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
 }
 
@@ -846,35 +869,79 @@ possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
        case GDK_ISO_Left_Tab:
                fakekey = GDK_nabla;
                break;
-               
+
        case GDK_Up:
                fakekey = GDK_uparrow;
                break;
-               
+
        case GDK_Down:
                fakekey = GDK_downarrow;
                break;
-               
+
        case GDK_Right:
                fakekey = GDK_rightarrow;
                break;
-               
+
        case GDK_Left:
                fakekey = GDK_leftarrow;
                break;
-               
+
+       case GDK_Return:
+               fakekey = GDK_3270_Enter;
+               break;
+
+       case GDK_KP_Enter:
+               fakekey = GDK_F35;
+               break;
+
        default:
                break;
        }
-       
+
        if (fakekey != GDK_VoidSymbol) {
                keyval = fakekey;
                return true;
-       } 
+       }
 
        return false;
 }
-               
+
+uint32_t
+possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
+{
+       switch (keyval) {
+       case GDK_nabla:
+               return GDK_Tab;
+               break;
+
+       case GDK_uparrow:
+               return GDK_Up;
+               break;
+
+       case GDK_downarrow:
+               return GDK_Down;
+               break;
+
+       case GDK_rightarrow:
+               return GDK_Right;
+               break;
+
+       case GDK_leftarrow:
+               return GDK_Left;
+               break;
+
+       case GDK_3270_Enter:
+               return GDK_Return;
+
+       case GDK_F35:
+               return GDK_KP_Enter;
+               break;
+       }
+
+       return keyval;
+}
+
+
 
 inline guint8
 convert_color_channel (guint8 src,
@@ -891,7 +958,7 @@ convert_bgra_to_rgba (guint8 const* src,
 {
        guint8 const* src_pixel = src;
        guint8*       dst_pixel = dst;
-       
+
        for (int y = 0; y < height; y++)
                for (int x = 0; x < width; x++)
                {
@@ -902,8 +969,113 @@ convert_bgra_to_rgba (guint8 const* src,
                        dst_pixel[2] = convert_color_channel (src_pixel[0],
                                                              src_pixel[3]);
                        dst_pixel[3] = src_pixel[3];
-                       
+
                        dst_pixel += 4;
                        src_pixel += 4;
                }
 }
+
+void
+resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
+{
+       Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
+       Gdk::Rectangle monitor_rect;
+       screen->get_monitor_geometry (0, monitor_rect);
+
+       int const w = std::min (monitor_rect.get_width(), max_width) * 0.8;
+       int const h = std::min (monitor_rect.get_height(), max_height) * 0.8;
+
+       window->resize (w, h);
+}
+
+Glib::RefPtr<Gdk::Pixbuf>
+pixbuf_from_ustring(const ustring& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg)
+{
+       static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
+
+       if (name.empty()) {
+               if (empty_pixbuf == 0) {
+                       empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
+                       *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
+               }
+               return *empty_pixbuf;
+       }
+
+       Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
+
+       cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
+       cairo_t* cr = cairo_create (surface);
+       cairo_text_extents_t te;
+       
+       cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
+       cairo_select_font_face (cr, font->get_family().c_str(),
+                               CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+       cairo_set_font_size (cr,  font->get_size() / Pango::SCALE);
+       cairo_text_extents (cr, name.c_str(), &te);
+       
+       cairo_move_to (cr, 0.5, 0.5 - te.height / 2 - te.y_bearing + clip_height / 2);
+       cairo_show_text (cr, name.c_str());
+       
+       convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
+
+       cairo_destroy(cr);
+       cairo_surface_destroy(surface);
+
+       return buf;
+}
+
+/** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
+string
+escape_underscores (string const & s)
+{
+       string o;
+       string::size_type const N = s.length ();
+
+       for (string::size_type i = 0; i < N; ++i) {
+               if (s[i] == '_') {
+                       o += "__";
+               } else {
+                       o += s[i];
+               }
+       }
+
+       return o;
+}
+
+static void
+adjustment_to_controllable (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
+{
+        boost::shared_ptr<Controllable> cont = wcont.lock();
+
+        if (cont) {
+                double val = adj->get_value();
+                if (val != cont->get_value()) {
+                        cont->set_value (val);
+                }
+        }
+}
+
+static void
+controllable_to_adjustment (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
+{
+        boost::shared_ptr<Controllable> cont = wcont.lock();
+
+        if (cont) {
+                float val = cont->get_value();
+                
+                if (val != adj->get_value()) {
+                        adj->set_value (val);
+                }
+        }
+}
+
+void
+control_link (ScopedConnectionList& scl, boost::shared_ptr<Controllable> c, Gtk::Adjustment& a)
+{
+        boost::weak_ptr<Controllable> wc (c);
+
+        a.signal_value_changed().connect (sigc::bind (sigc::ptr_fun (adjustment_to_controllable), &a, wc));
+        c->Changed.connect (scl, MISSING_INVALIDATOR, boost::bind (controllable_to_adjustment, &a, wc),
+                            gui_context());
+}
+