Use Session::session_directory to access the sound path of the session in Editor...
[ardour.git] / gtk2_ardour / utils.cc
index c0f1a9eaa878fcd5f086f02785fc154d6df70211..d574ece427f4827d807671e22e0ad70b67ebe29c 100644 (file)
@@ -15,7 +15,6 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    $Id$
 */
 
 #include <cstdlib>
@@ -43,123 +42,64 @@ 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)
+int
+pixel_width (const ustring& str, Pango::FontDescription& font)
 {
-       /* 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, "");
-       }
+       Label foo;
+       Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
 
-       /* remove upper-case consonants, starting at end */
+       layout->set_font_description (font);
+       layout->set_text (str);
 
-       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;
+       int width, height;
+       Gtkmm2ext::get_ink_pixel_size (layout, width, height);
+       return width;
 }
 
-string
-fit_to_pixels (const string & str, int pixel_width, const string & font)
+ustring
+fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
 {
        Label foo;
-       int width;
-       int height;
-       Pango::FontDescription fontdesc (font);
-       
-       int namelen = str.length();
-       char cstr[namelen+1];
-       strcpy (cstr, str.c_str());
-       
-       while (namelen) {
-               Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (cstr);
+       Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
+       ustring::size_type shorter_by = 0;
+       ustring txt;
 
-               layout->set_font_description (fontdesc);
-               layout->get_pixel_size (width, height);
+       layout->set_font_description (font);
 
-               if (width < (pixel_width)) {
-                       break;
-               }
+       actual_width = 0;
 
-               --namelen;
-               cstr[namelen] = '\0';
+       ustring ustr = str;
+       ustring::iterator last = ustr.end();
+       --last; /* now points at final entry */
 
-       }
+       txt = ustr;
 
-       return cstr;
-}
+       while (!ustr.empty()) {
 
-int
-atoi (const string& s)
-{
-       return atoi (s.c_str());
-}
+               layout->set_text (txt);
 
-double
-atof (const string& s)
-{
-       return atof (s.c_str());
-}
+               int width, height;
+               Gtkmm2ext::get_ink_pixel_size (layout, width, height);
 
-vector<string>
-internationalize (const char **array)
-{
-       vector<string> v;
+               if (width < pixel_width) {
+                       actual_width = width;
+                       break;
+               }
+               
+               ustr.erase (last--);
+               shorter_by++;
 
-       for (uint32_t i = 0; array[i]; ++i) {
-               v.push_back (_(array[i]));
+               if (with_ellipses && shorter_by > 3) {
+                       txt = ustr;
+                       txt += "...";
+               } else {
+                       txt = ustr;
+               }
        }
 
-       return v;
+       return txt;
 }
 
 gint
@@ -193,7 +133,7 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
                return 0;
        }
 
-       savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
+       savergb = rgb = (unsigned char*) malloc (h * w * 3);
        
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
        for (t = 0; t < colors; ++t) {
@@ -236,7 +176,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
                return 0;
        }
 
-       savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
+       savergb = rgb = (unsigned char*) malloc (h * w * 4);
        
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
 
@@ -294,152 +234,35 @@ get_canvas_points (string who, uint32_t npoints)
        return new ArdourCanvas::Points (npoints);
 }
 
-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;
-               }
-       }
-}
-
 Pango::FontDescription
 get_font_for_style (string widgetname)
 {
        Gtk::Window window (WINDOW_TOPLEVEL);
        Gtk::Label foobar;
-       Glib::RefPtr<Style> style;
+       Glib::RefPtr<Gtk::Style> style;
 
        window.add (foobar);
        foobar.set_name (widgetname);
        foobar.ensure_style();
 
        style = foobar.get_style ();
-       return style->get_font();
-}
-
-gint
-pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
-{
-       if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
-               return FALSE;
-       }
 
-       if (Keyboard::is_delete_event (ev)) {
-
-               gint pos;
-               gint cmp;
+       Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
+       
+       PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
+       
+       if (!pfd) {
                
-               pos = pane->get_position ();
-
-               if (dynamic_cast<VPaned*>(pane)) {
-                       cmp = pane->get_height();
-               } else {
-                       cmp = pane->get_width();
-               }
-
-               /* we have to use approximations here because we can't predict the
-                  exact position or sizes of the pane (themes, etc)
-               */
-
-               if (pos < 10 || abs (pos - cmp) < 10) {
-
-                       /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
-                       
-                       pane->set_position ((intptr_t) pane->get_data ("rpos"));
-
-               } else {        
+               /* layout inherited its font description from a PangoContext */
 
-                       int collapse_direction;
-
-                       /* store the current position */
-
-                       pane->set_data ("rpos", (gpointer) pos);
-
-                       /* collapse to show the relevant child in full */
-                       
-                       collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
-
-                       if (collapse_direction) {
-                               pane->set_position (1);
-                       } else {
-                               if (dynamic_cast<VPaned*>(pane)) {
-                                       pane->set_position (pane->get_height());
-                               } else {
-                                       pane->set_position (pane->get_width());
-                               }
-                       }
-               }
-
-               return TRUE;
+               PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
+               pfd =  pango_context_get_font_description (ctxt);
+               return Pango::FontDescription (pfd, true); /* make a copy */
        } 
 
-       return FALSE;
+       return Pango::FontDescription (pfd, true); /* make a copy */
 }
+
 uint32_t
 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
 {
@@ -467,6 +290,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;
@@ -517,12 +341,25 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
        GtkWidget* focus = gtk_window_get_focus (win);
        bool special_handling_of_unmodified_accelerators = false;
 
+#undef  DEBUG_ACCELERATOR_HANDLING
+#ifdef  DEBUG_ACCELERATOR_HANDLING
+       bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
+#endif
+
        if (focus) {
-               if (GTK_IS_ENTRY(focus)) {
+               if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
                        special_handling_of_unmodified_accelerators = true;
-               }
+               } 
        } 
 
+#ifdef DEBUG_ACCELERATOR_HANDLING
+       if (debug) {
+               cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? " 
+                    << special_handling_of_unmodified_accelerators
+                    << endl;
+       }
+#endif
+
        /* This exists to allow us to override the way GTK handles
           key events. The normal sequence is:
 
@@ -554,31 +391,96 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
           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)) {
+                        Gdk::CONTROL_MASK)) {
 
                /* no special handling or modifiers in effect: accelerate first */
 
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\tactivate, then propagate\n";
+               }
+#endif
                if (!gtk_window_activate_key (win, ev)) {
                        return gtk_window_propagate_key_event (win, ev);
                } else {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\tnot handled\n";
+               }
+#endif
                        return true;
                } 
        }
        
        /* no modifiers, propagate first */
        
+#ifdef DEBUG_ACCELERATOR_HANDLING
+       if (debug) {
+               cerr << "\tpropagate, then activate\n";
+       }
+#endif
        if (!gtk_window_propagate_key_event (win, ev)) {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\tpropagation didn't handle, so activate\n";
+               }
+#endif
                return gtk_window_activate_key (win, ev);
-       } 
-
+       } else {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\thandled by propagate\n";
+               }
+#endif
+               return true;
+       }
 
+#ifdef DEBUG_ACCELERATOR_HANDLING
+       if (debug) {
+               cerr << "\tnot handled\n";
+       }
+#endif
        return true;
 }
 
@@ -592,3 +494,97 @@ get_xpm (std::string name)
        return (xpm_map[name]);
 }
 
+Glib::RefPtr<Gdk::Pixbuf>      
+get_icon (const char* cname)
+{
+       string name = cname;
+       name += X_(".png");
+
+       string path = ARDOUR::find_data_file (name, "icons");
+
+       if (path.empty()) {
+               fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
+               /*NOTREACHED*/
+       }
+
+       return Gdk::Pixbuf::create_from_file (path);
+}
+
+string
+longest (vector<string>& strings)
+{
+       if (strings.empty()) {
+               return string ("");
+       }
+
+       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;
+}
+
+bool
+key_is_legal_for_numeric_entry (guint keyval)
+{
+       switch (keyval) {
+       case GDK_minus:
+       case GDK_plus:
+       case GDK_period:
+       case GDK_comma:
+       case GDK_0:
+       case GDK_1:
+       case GDK_2:
+       case GDK_3:
+       case GDK_4:
+       case GDK_5:
+       case GDK_6:
+       case GDK_7:
+       case GDK_8:
+       case GDK_9:
+       case GDK_KP_Add:
+       case GDK_KP_Subtract:
+       case GDK_KP_Decimal:
+       case GDK_KP_0:
+       case GDK_KP_1:
+       case GDK_KP_2:
+       case GDK_KP_3:
+       case GDK_KP_4:
+       case GDK_KP_5:
+       case GDK_KP_6:
+       case GDK_KP_7:
+       case GDK_KP_8:
+       case GDK_KP_9:
+       case GDK_Return:
+       case GDK_BackSpace:
+       case GDK_Delete:
+       case GDK_KP_Enter:
+       case GDK_Home:
+       case GDK_End:
+       case GDK_Left:
+       case GDK_Right:
+               return true;
+               
+       default:
+               break;
+       }
+
+       return false;
+}
+
+
+