make waveforms centered when height hits NAME_HIGHLIGHT_THRESHOLD
[ardour.git] / gtk2_ardour / utils.cc
index bccdeb22bc233d59da8def650cb2ef34a371ef2f..91cb7ad58b21bf30c595cfbe9d3710b05428d326 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <cstdlib>
 #include <cctype>
+#include <fstream>
+#include <sys/stat.h>
 #include <libart_lgpl/art_misc.h>
 #include <gtkmm/window.h>
 #include <gtkmm/combo.h>
 #include <gtk/gtkpaned.h>
 
 #include <gtkmm2ext/utils.h>
+#include <ardour/ardour.h>
 
 #include "ardour_ui.h"
+#include "keyboard.h"
 #include "utils.h"
 #include "i18n.h"
 #include "rgb_macros.h"
+#include "canvas_impl.h"
 
 using namespace std;
 using namespace Gtk;
 using namespace sigc;
+using namespace Glib;
+using namespace PBD;
 
-string
-short_version (string orig, string::size_type target_length)
-{
-       /* this tries to create a recognizable abbreviation
-          of "orig" by removing characters until we meet
-          a certain target length.
-
-          note that we deliberately leave digits in the result
-          without modification.
-       */
-
-
-       string::size_type pos;
-
-       /* remove white-space and punctuation, starting at end */
-
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
-
-       /* remove lower-case vowels, starting at end */
-
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
-
-       /* remove upper-case vowels, starting at end */
-
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
-
-       /* remove lower-case consonants, starting at end */
-
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
-
-       /* remove upper-case consonants, starting at end */
-
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
-
-       /* whatever the length is now, use it */
-       
-       return orig;
-}
-
-string
-fit_to_pixels (string str, int pixel_width, string font)
+ustring
+fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width)
 {
        Label foo;
-       int width;
-       int height;
-       Pango::FontDescription fontdesc (font);
-       
-       int namelen = str.length();
-       char cstr[namelen+1];
-       strcpy (cstr, str.c_str());
+       Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
        
-       while (namelen) {
-               Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (cstr);
-
-               layout->set_font_description (fontdesc);
-               layout->get_pixel_size (width, height);
-
-               if (width < (pixel_width)) {
-                       break;
-               }
+       layout->set_font_description (font);
 
-               --namelen;
-               cstr[namelen] = '\0';
+       actual_width = 0;
 
-       }
-
-       return cstr;
-}
-
-int
-atoi (const string& s)
-{
-       return atoi (s.c_str());
-}
-
-double
-atof (const string& s)
-{
-       return atof (s.c_str());
-}
-
-void
-strip_whitespace_edges (string& str)
-{
-       string::size_type i;
-       string::size_type len;
-       string::size_type s;
+       ustring ustr = str;
+       ustring::iterator last = ustr.end();
+       --last; /* now points at final entry */
 
-       len = str.length();
+       while (!ustr.empty()) {
 
-       for (i = 0; i < len; ++i) {
-               if (isgraph (str[i])) {
-                       break;
-               }
-       }
+               layout->set_text (ustr);
 
-       s = i;
+               int width, height;
+               Gtkmm2ext::get_ink_pixel_size (layout, width, height);
 
-       for (i = len - 1; i >= 0; --i) {
-               if (isgraph (str[i])) {
+               if (width < pixel_width) {
+                       actual_width = width;
                        break;
                }
+               
+               ustr.erase (last);
+               --last;
        }
 
-       str = str.substr (s, (i - s) + 1);
-}
-
-vector<string>
-internationalize (const char **array)
-{
-       vector<string> v;
-
-       for (uint32_t i = 0; array[i]; ++i) {
-               v.push_back (_(array[i]));
-       }
-
-       return v;
+       return ustr;
 }
 
 gint
@@ -302,7 +198,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
        return (savergb);
 }
 
-Gnome::Canvas::Points*
+ArdourCanvas::Points*
 get_canvas_points (string who, uint32_t npoints)
 {
        // cerr << who << ": wants " << npoints << " canvas points" << endl;
@@ -311,105 +207,17 @@ get_canvas_points (string who, uint32_t npoints)
                abort ();
        }
 #endif
-       return new Gnome::Canvas::Points (npoints);
-}
-
-int
-channel_combo_get_channel_count (Gtk::Combo& combo)
-{
-       string str = combo.get_entry()->get_text();
-       int chns;
-
-       if (str == _("mono")) {
-               return 1;
-       } else if (str == _("stereo")) {
-               return 2;
-       } else if ((chns = atoi (str)) != 0) {
-               return chns;
-       } else {
-               return 0;
-       }
-}
-
-static int32_t 
-int_from_hex (char hic, char loc) 
-{
-       int hi;         /* hi byte */
-       int lo;         /* low byte */
-
-       hi = (int) hic;
-
-       if( ('0'<=hi) && (hi<='9') ) {
-               hi -= '0';
-       } else if( ('a'<= hi) && (hi<= 'f') ) {
-               hi -= ('a'-10);
-       } else if( ('A'<=hi) && (hi<='F') ) {
-               hi -= ('A'-10);
-       }
-       
-       lo = (int) loc;
-       
-       if( ('0'<=lo) && (lo<='9') ) {
-               lo -= '0';
-       } else if( ('a'<=lo) && (lo<='f') ) {
-               lo -= ('a'-10);
-       } else if( ('A'<=lo) && (lo<='F') ) {
-               lo -= ('A'-10);
-       }
-
-       return lo + (16 * hi);
-}
-
-void
-url_decode (string& url)
-{
-       string::iterator last;
-       string::iterator next;
-
-       for (string::iterator i = url.begin(); i != url.end(); ++i) {
-               if ((*i) == '+') {
-                       *i = ' ';
-               }
-       }
-
-       if (url.length() <= 3) {
-               return;
-       }
-
-       last = url.end();
-
-       --last; /* points at last char */
-       --last; /* points at last char - 1 */
-
-       for (string::iterator i = url.begin(); i != last; ) {
-
-               if (*i == '%') {
-
-                       next = i;
-
-                       url.erase (i);
-                       
-                       i = next;
-                       ++next;
-                       
-                       if (isxdigit (*i) && isxdigit (*next)) {
-                               /* replace first digit with char */
-                               *i = int_from_hex (*i,*next);
-                               ++i; /* points at 2nd of 2 digits */
-                               url.erase (i);
-                       }
-               } else {
-                       ++i;
-               }
-       }
+       return new ArdourCanvas::Points (npoints);
 }
 
 Pango::FontDescription
 get_font_for_style (string widgetname)
 {
+       Gtk::Window window (WINDOW_TOPLEVEL);
        Gtk::Label foobar;
        Glib::RefPtr<Style> style;
 
+       window.add (foobar);
        foobar.set_name (widgetname);
        foobar.ensure_style();
 
@@ -445,7 +253,7 @@ pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
 
                        /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
                        
-                       pane->set_position ((gint64) pane->get_data ("rpos"));
+                       pane->set_position ((intptr_t) pane->get_data ("rpos"));
 
                } else {        
 
@@ -457,7 +265,7 @@ pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
 
                        /* collapse to show the relevant child in full */
                        
-                       collapse_direction = (gint64) pane->get_data ("collapse-direction");
+                       collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
 
                        if (collapse_direction) {
                                pane->set_position (1);
@@ -476,33 +284,190 @@ pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
        return FALSE;
 }
 uint32_t
-rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a)
+rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
 {
+       /* In GTK+2, styles aren't set up correctly if the widget is not
+          attached to a toplevel window that has a screen pointer.
+       */
+
+       static Gtk::Window* window = 0;
+
+       if (window == 0) {
+               window = new Window (WINDOW_TOPLEVEL);
+       }
+
        Gtk::Label foo;
        
+       window->add (foo);
+
        foo.set_name (style);
        foo.ensure_style ();
        
        GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
 
        if (waverc) {
-               r = waverc->fg[Gtk::STATE_NORMAL].red / 257;
-               g = waverc->fg[Gtk::STATE_NORMAL].green / 257;
-               b = waverc->fg[Gtk::STATE_NORMAL].blue / 257;
-
-               /* what a hack ... "a" is for "active" */
-
-               a = waverc->fg[GTK_STATE_ACTIVE].red / 257; 
-
+               if (attr == "fg") {
+                       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;
+                       }
+               } else if (attr == "bg") {
+                       r = g = b = 0;
+                       r = waverc->bg[state].red / 257;
+                       g = waverc->bg[state].green / 257;
+                       b = waverc->bg[state].blue / 257;
+               } else if (attr == "base") {
+                       r = waverc->base[state].red / 257;
+                       g = waverc->base[state].green / 257;
+                       b = waverc->base[state].blue / 257;
+               } else if (attr == "text") {
+                       r = waverc->text[state].red / 257;
+                       g = waverc->text[state].green / 257;
+                       b = waverc->text[state].blue / 257;
+               }
        } else {
                warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
        }
+
+       window->remove ();
        
-       return (uint32_t) RGBA_TO_UINT(r,g,b,a);
+       if (state == Gtk::STATE_NORMAL && rgba) {
+               return (uint32_t) RGBA_TO_UINT(r,g,b,a);
+       } else {
+               return (uint32_t) RGB_TO_UINT(r,g,b);
+       }
+}
+
+bool 
+canvas_item_visible (ArdourCanvas::Item* item)
+{
+       return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
 }
 
 void
-decorate (Gtk::Window& w, Gdk::WMDecoration d)
+set_color (Gdk::Color& c, int rgb)
+{
+       c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
+}
+
+bool
+key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 {
-       w.get_window()->set_decorations (d);
+       GtkWindow* win = window.gobj();
+       GtkWidget* focus = gtk_window_get_focus (win);
+       bool special_handling_of_unmodified_accelerators = false;
+
+       if (focus) {
+               if (GTK_IS_ENTRY(focus)) {
+                       special_handling_of_unmodified_accelerators = true;
+               }
+       } 
+
+       /* This exists to allow us to override the way GTK handles
+          key events. The normal sequence is:
+
+          a) event is delivered to a GtkWindow
+          b) accelerators/mnemonics are activated
+          c) if (b) didn't handle the event, propagate to
+              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 
+          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 
+          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
+          from acceleration.
+
+          Of course, this can also be problematic: if there
+          is a widget with focus, then it will swallow
+          all "normal text" accelerators.
+       */
+
+
+       if (!special_handling_of_unmodified_accelerators) {
+
+               /* pretend that certain key events that GTK does not allow
+                  to be used as accelerators are actually something that
+                  it does allow.
+               */
+
+               int ret = false;
+
+               switch (ev->keyval) {
+               case GDK_Up:
+                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
+                       break;
+
+               case GDK_Down:
+                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
+                       break;
+
+               case GDK_Right:
+                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
+                       break;
+
+               case GDK_Left:
+                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
+                       break;
+
+               default:
+                       break;
+               }
+
+               if (ret) {
+                       return true;
+               }
+       }
+               
+       if (!special_handling_of_unmodified_accelerators ||
+           ev->state & (Gdk::MOD1_MASK|
+                        Gdk::MOD2_MASK|
+                        Gdk::MOD3_MASK|
+                        Gdk::MOD4_MASK|
+                        Gdk::MOD5_MASK|
+                        Gdk::CONTROL_MASK|
+                        Gdk::LOCK_MASK)) {
+
+               /* no special handling or modifiers in effect: accelerate first */
+
+               if (!gtk_window_activate_key (win, ev)) {
+                       return gtk_window_propagate_key_event (win, ev);
+               } else {
+                       return true;
+               } 
+       }
+       
+       /* no modifiers, propagate first */
+       
+       if (!gtk_window_propagate_key_event (win, ev)) {
+               return gtk_window_activate_key (win, ev);
+       } 
+
+
+       return true;
 }
+
+Glib::RefPtr<Gdk::Pixbuf>      
+get_xpm (std::string name)
+{
+       if (!xpm_map[name]) {
+               xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
+       }
+               
+       return (xpm_map[name]);
+}
+