X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Futils.cc;h=0f53b0912f27f05232eef85a7d603c119a1617f4;hb=ccf58b8de23619db9983a45f696fd97e13a15cce;hp=ee124db50ffde8d30c688752501c84111f0a6ba3;hpb=14d43ca9fe28a8309b4a52fa85e2b0c64a92248b;p=ardour.git diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index ee124db50f..0f53b0912f 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -1,7 +1,7 @@ /* - Copyright (C) 2003 Paul Davis + Copyright (C) 2003 Paul Davis - This program is free software; you can redistribute it and/or modify + 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. @@ -15,189 +15,161 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ +#include // for fontmap resolution control for GnomeCanvas +#include // for fontmap resolution control for GnomeCanvas + #include #include +#include +#include #include +#include #include #include #include #include #include +#include "pbd/file_utils.h" + #include +#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" 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. - */ +sigc::signal DPIReset; +int +pixel_width (const ustring& str, Pango::FontDescription& font) +{ + Label foo; + Glib::RefPtr layout = foo.create_pango_layout (""); - string::size_type pos; + layout->set_font_description (font); + layout->set_text (str); - /* remove white-space and punctuation, starting at end */ + int width, height; + Gtkmm2ext::get_ink_pixel_size (layout, width, height); + return width; +} - while (orig.length() > target_length) { - if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) { - break; - } - orig.replace (pos, 1, ""); - } +ustring +fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses) +{ + Label foo; + Glib::RefPtr layout = foo.create_pango_layout (""); + ustring::size_type shorter_by = 0; + ustring txt; - /* remove lower-case vowels, starting at end */ + layout->set_font_description (font); - while (orig.length() > target_length) { - if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) { - break; - } - orig.replace (pos, 1, ""); - } + actual_width = 0; - /* remove upper-case vowels, starting at end */ + ustring ustr = str; + ustring::iterator last = ustr.end(); + --last; /* now points at final entry */ - while (orig.length() > target_length) { - if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) { - break; - } - orig.replace (pos, 1, ""); - } + txt = ustr; - /* remove lower-case consonants, starting at end */ + while (!ustr.empty()) { - while (orig.length() > target_length) { - if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) { - break; - } - orig.replace (pos, 1, ""); - } + layout->set_text (txt); - /* remove upper-case consonants, starting at end */ + int width, height; + Gtkmm2ext::get_ink_pixel_size (layout, width, height); - while (orig.length() > target_length) { - if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) { + if (width < pixel_width) { + actual_width = width; break; } - orig.replace (pos, 1, ""); - } - /* whatever the length is now, use it */ - - return orig; -} + ustr.erase (last--); + shorter_by++; -string -fit_to_pixels (string str, int32_t pixel_width, Gdk_Font& font) -{ - gint width; - gint lbearing; - gint rbearing; - gint ascent; - gint descent; - - int namelen = str.length(); - char cstr[namelen+1]; - strcpy (cstr, str.c_str()); - - while (namelen) { - - gdk_string_extents (font, - cstr, - &lbearing, - &rbearing, - &width, - &ascent, - &descent); - - if (width < (pixel_width)) { - break; + if (with_ellipses && shorter_by > 3) { + txt = ustr; + txt += "..."; + } else { + txt = ustr; } - - --namelen; - cstr[namelen] = '\0'; - } - return cstr; + return txt; } -int -atoi (const string& s) -{ - return atoi (s.c_str()); -} +/** Try to fit a string into a given horizontal space by ellipsizing it. + * @param cr Cairo context in which the text will be plotted. + * @param name Text. + * @param avail Available horizontal space. + * @return (Text, possibly ellipsized) and (horizontal size of text) + */ -double -atof (const string& s) +std::pair +fit_to_pixels (cairo_t* cr, std::string name, double avail) { - return atof (s.c_str()); -} + /* XXX hopefully there exists a more efficient way of doing this */ -void -strip_whitespace_edges (string& str) -{ - string::size_type i; - string::size_type len; - string::size_type s; + bool abbreviated = false; + uint32_t width = 0; - len = str.length(); + while (1) { + cairo_text_extents_t ext; + cairo_text_extents (cr, name.c_str(), &ext); - for (i = 0; i < len; ++i) { - if (isgraph (str[i])) { + if (ext.width < avail || name.length() <= 4) { + width = ext.width; break; } - } - - s = i; - for (i = len - 1; i >= 0; --i) { - if (isgraph (str[i])) { - break; + if (abbreviated) { + name = name.substr (0, name.length() - 4) + "..."; + } else { + name = name.substr (0, name.length() - 3) + "..."; + abbreviated = true; } } - str = str.substr (s, (i - s) + 1); + return std::make_pair (name, width); } -vector -internationalize (const char **array) -{ - vector v; - for (uint32_t i = 0; array[i]; ++i) { - v.push_back (_(array[i])); +/** Add an element to a menu, settings its sensitivity. + * @param m Menu to add to. + * @param e Element to add. + * @param s true to make sensitive, false to make insensitive + */ +void +add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s) +{ + m.push_back (e); + if (!s) { + m.back().set_sensitive (false); } - - return v; } -gint -just_hide_it (GdkEventAny *ev, Gtk::Window *win) -{ - ARDOUR_UI::instance()->allow_focus (false); - win->hide_all (); - return TRUE; -} -void -allow_keyboard_focus (bool yn) +gint +just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win) { - ARDOUR_UI::instance()->allow_focus (yn); + win->hide (); + return 0; } /* xpm2rgb copied from nixieclock, which bore the legend: @@ -215,23 +187,23 @@ 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 << compose (_("bad XPM header %1"), xpm[0]) + error << string_compose (_("bad XPM header %1"), xpm[0]) << endmsg; 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) { sscanf (xpm[t+1], "%c c #%lx", &c, &val); vals[c] = val; } - + // COLORMAP -> RGB CONVERSION // Get low 3 bytes from vals[] // @@ -262,13 +234,13 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h) // PARSE HEADER if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) { - error << compose (_("bad XPM header %1"), xpm[0]) + error << string_compose (_("bad XPM header %1"), xpm[0]) << endmsg; 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 if (strstr (xpm[1], "None")) { @@ -283,7 +255,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[] // @@ -313,8 +285,8 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h) return (savergb); } -GnomeCanvasPoints* -get_canvas_points (string who, uint32_t npoints) +ArdourCanvas::Points* +get_canvas_points (string /*who*/, uint32_t npoints) { // cerr << who << ": wants " << npoints << " canvas points" << endl; #ifdef TRAP_EXCESSIVE_POINT_REQUESTS @@ -322,199 +294,682 @@ get_canvas_points (string who, uint32_t npoints) abort (); } #endif - return gnome_canvas_points_new (npoints); + return new ArdourCanvas::Points (npoints); } -int -channel_combo_get_channel_count (Gtk::Combo& combo) +Pango::FontDescription* +get_font_for_style (string widgetname) { - 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; + Gtk::Window window (WINDOW_TOPLEVEL); + Gtk::Label foobar; + Glib::RefPtr style; + + window.add (foobar); + foobar.set_name (widgetname); + foobar.ensure_style(); + + style = foobar.get_style (); + + Glib::RefPtr 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 */ +} + +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) +{ + /* 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) { + 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 { - return 0; + warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl; + } + + window->remove (); + + 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); } } -static int32_t -int_from_hex (char hic, char loc) + +Gdk::Color +color_from_style (string widget_style_name, int state, string attr) { - int hi; /* hi byte */ - int lo; /* low byte */ + GtkStyle* style; - hi = (int) hic; + style = gtk_rc_get_style_by_paths (gtk_settings_get_default(), + widget_style_name.c_str(), + 0, G_TYPE_NONE); - 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); + if (!style) { + error << string_compose (_("no style found for %1, using red"), style) << endmsg; + return Gdk::Color ("red"); } - - 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); + + if (attr == "fg") { + return Gdk::Color (&style->fg[state]); + } + + if (attr == "bg") { + return Gdk::Color (&style->bg[state]); + } + + if (attr == "light") { + return Gdk::Color (&style->light[state]); + } + + if (attr == "dark") { + return Gdk::Color (&style->dark[state]); + } + + if (attr == "mid") { + return Gdk::Color (&style->mid[state]); + } + + if (attr == "text") { + return Gdk::Color (&style->text[state]); + } + + if (attr == "base") { + return Gdk::Color (&style->base[state]); } - return lo + (16 * hi); + if (attr == "text_aa") { + return Gdk::Color (&style->text_aa[state]); + } + + error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg; + return Gdk::Color ("red"); +} + +Glib::RefPtr +gc_from_style (string widget_style_name, int state, string attr) +{ + GtkStyle* style; + + style = gtk_rc_get_style_by_paths (gtk_settings_get_default(), + widget_style_name.c_str(), + 0, G_TYPE_NONE); + + if (!style) { + error << string_compose (_("no style found for %1, using red"), style) << endmsg; + Glib::RefPtr ret = Gdk::GC::create(); + ret->set_rgb_fg_color(Gdk::Color("red")); + return ret; + } + + if (attr == "fg") { + return Glib::wrap(style->fg_gc[state]); + } + + if (attr == "bg") { + return Glib::wrap(style->bg_gc[state]); + } + + if (attr == "light") { + return Glib::wrap(style->light_gc[state]); + } + + if (attr == "dark") { + return Glib::wrap(style->dark_gc[state]); + } + + if (attr == "mid") { + return Glib::wrap(style->mid_gc[state]); + } + + if (attr == "text") { + return Glib::wrap(style->text_gc[state]); + } + + if (attr == "base") { + return Glib::wrap(style->base_gc[state]); + } + + if (attr == "text_aa") { + return Glib::wrap(style->text_aa_gc[state]); + } + + error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg; + Glib::RefPtr ret = Gdk::GC::create(); + ret->set_rgb_fg_color(Gdk::Color("red")); + return ret; +} + + +bool +canvas_item_visible (ArdourCanvas::Item* item) +{ + return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false; } void -url_decode (string& url) +set_color (Gdk::Color& c, int rgb) { - string::iterator last; - string::iterator next; + c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256); +} + +#ifdef GTKOSX +extern "C" { + gboolean gdk_quartz_possibly_forward (GdkEvent*); +} +#endif - for (string::iterator i = url.begin(); i != url.end(); ++i) { - if ((*i) == '+') { - *i = ' '; +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 +key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) +{ + GtkWindow* win = window.gobj(); + GtkWidget* focus = gtk_window_get_focus (win); + bool special_handling_of_unmodified_accelerators = false; + bool allow_activating = true; + +#undef DEBUG_ACCELERATOR_HANDLING +#ifdef DEBUG_ACCELERATOR_HANDLING + //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0); + bool debug=true; +#endif + if (focus) { + if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) { + special_handling_of_unmodified_accelerators = true; } } - if (url.length() <= 3) { - return; +#ifdef GTKOSX + /* should this be universally true? */ + if (Keyboard::some_magic_widget_has_focus ()) { + allow_activating = false; } +#endif - last = url.end(); +#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 + << " magic widget focus ? " + << Keyboard::some_magic_widget_has_focus() + << " allow_activation ? " + << allow_activating + << endl; + } +#endif - --last; /* points at last char */ - --last; /* points at last char - 1 */ + /* 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. + */ - for (string::iterator i = url.begin(); i != last; ) { - if (*i == '%') { + if (!special_handling_of_unmodified_accelerators) { - next = i; + /* pretend that certain key events that GTK does not allow + to be used as accelerators are actually something that + it does allow. + */ - 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); + uint32_t fakekey = ev->keyval; + + if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) { + if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) { + return true; } - } else { - ++i; + +#ifdef GTKOSX + if (allow_activating) { + int oldval = ev->keyval; + ev->keyval = fakekey; + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } + ev->keyval = oldval; + } +#endif + } + } + + /* consider all relevant modifiers but not LOCK or SHIFT */ + + guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK)); + + if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) { + + /* no special handling or there are modifiers in effect: accelerate first */ + +#ifdef DEBUG_ACCELERATOR_HANDLING + if (debug) { + cerr << "\tactivate, then propagate\n"; } +#endif + + if (allow_activating) { +#ifdef GTKOSX + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } +#endif + if (gtk_window_activate_key (win, ev)) { + return true; + } + } + +#ifdef DEBUG_ACCELERATOR_HANDLING + if (debug) { + cerr << "\tnot accelerated, now propagate\n"; + } +#endif + return gtk_window_propagate_key_event (win, ev); + } + + /* 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 + + 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) { + cerr << "\thandled by propagate\n"; + } +#endif + return true; + } + +#ifdef DEBUG_ACCELERATOR_HANDLING + if (debug) { + cerr << "\tnot handled\n"; + } +#endif + return true; } -string -get_font_for_style (string widgetname) +Glib::RefPtr +get_xpm (std::string name) { - Gtk::Label foobar; + if (!xpm_map[name]) { - foobar.set_name (widgetname); - foobar.ensure_style(); + 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; + } - if (foobar.get_style() == 0 || foobar.get_style()->gobj()->rc_style == 0 || foobar.get_style()->gobj()->rc_style->font_name == 0) { - return "fixed"; + try { + xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string()); + } catch(const Glib::Error& e) { + warning << "Caught Glib::Error: " << e.what() << endmsg; + } } - string str = foobar.get_style()->gobj()->rc_style->font_name; + return xpm_map[name]; +} - if (str.empty()) { - return "fixed"; // standard X Window fallback font - } else { - return str; +Glib::ustring +get_icon_path (const char* cname) +{ + string name = cname; + name += X_(".png"); + + SearchPath spath(ARDOUR::ardour_search_path()); + spath += ARDOUR::system_data_search_path(); + + spath.add_subdirectory_to_paths("icons"); + + sys::path data_file_path; + + if (!find_file_in_search_path (spath, name, data_file_path)) { + fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg; } + + return data_file_path.to_string(); } -gint -pane_handler (GdkEventButton* ev, Gtk::Paned* pane) + +Glib::RefPtr +get_icon (const char* cname) { - if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) { - return FALSE; + Glib::RefPtr img; + try { + img = Gdk::Pixbuf::create_from_file (get_icon_path (cname)); + } catch (const Gdk::PixbufError &e) { + cerr << "Caught PixbufError: " << e.what() << endl; + } catch (...) { + g_message("Caught ... "); } - if (Keyboard::is_delete_event (ev)) { + return img; +} - gint pos; - gint cmp; +string +longest (vector& strings) +{ + if (strings.empty()) { + return string (""); + } - pos = Gtkmm2ext::gtk_paned_get_position (pane->gobj()); + vector::iterator longest = strings.begin(); + string::size_type longest_length = (*longest).length(); - if (dynamic_cast(pane)) { - cmp = pane->height(); - } else { - cmp = pane->width(); + vector::iterator i = longest; + ++i; + + while (i != strings.end()) { + + string::size_type len = (*i).length(); + + if (len > longest_length) { + longest = i; + longest_length = len; } - /* we have to use approximations here because we can't predict the - exact position or sizes of the pane (themes, etc) - */ + ++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; + } - if (pos < 10 || abs (pos - cmp) < 10) { + return false; +} +void +set_pango_fontsize () +{ + long val = ARDOUR::Config->get_font_scale(); - /* 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")); + /* FT2 rendering - used by GnomeCanvas, sigh */ - } else { + pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024); - int collapse_direction; + /* Cairo rendering, in case there is any */ - /* store the current position */ + pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024); +} - pane->set_data ("rpos", (gpointer) pos); +void +reset_dpi () +{ + long val = ARDOUR::Config->get_font_scale(); + set_pango_fontsize (); + /* Xft rendering */ - /* collapse to show the relevant child in full */ - - collapse_direction = (gint64) pane->get_data ("collapse-direction"); + gtk_settings_set_long_property (gtk_settings_get_default(), + "gtk-xft-dpi", val, "ardour"); + DPIReset();//Emit Signal +} - if (collapse_direction) { - pane->set_position (1); - } else { - if (dynamic_cast(pane)) { - pane->set_position (pane->height()); - } else { - pane->set_position (pane->width()); - } - } - } +bool +possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval) +{ + int fakekey = GDK_VoidSymbol; + + switch (keyval) { + case GDK_Tab: + 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; - return TRUE; - } + case GDK_Left: + fakekey = GDK_leftarrow; + break; - return FALSE; + default: + break; + } + + if (fakekey != GDK_VoidSymbol) { + keyval = fakekey; + return true; + } + + return false; } -uint32_t -rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a) + + +inline guint8 +convert_color_channel (guint8 src, + guint8 alpha) { - Gtk::Label foo; - - foo.set_name (style); - foo.ensure_style (); - - GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style; + return alpha ? ((guint (src) << 8) - src) / alpha : 0; +} - 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; +void +convert_bgra_to_rgba (guint8 const* src, + guint8* dst, + int width, + int height) +{ + guint8 const* src_pixel = src; + guint8* dst_pixel = dst; + + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + { + dst_pixel[0] = convert_color_channel (src_pixel[2], + src_pixel[3]); + dst_pixel[1] = convert_color_channel (src_pixel[1], + src_pixel[3]); + 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 screen = window->get_screen (); + Gdk::Rectangle monitor_rect; + screen->get_monitor_geometry (0, monitor_rect); - /* what a hack ... "a" is for "active" */ + 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; - a = waverc->fg[GTK_STATE_ACTIVE].red / 257; + window->resize (w, h); +} - } else { - warning << compose (_("missing RGBA style for \"%1\""), style) << endl; +Glib::RefPtr +pixbuf_from_ustring(const ustring& name, Pango::FontDescription* font, int clip_width, int clip_height) +{ + static Glib::RefPtr* empty_pixbuf = 0; + + if (name.empty()) { + if (empty_pixbuf == 0) { + empty_pixbuf = new Glib::RefPtr; + *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height); + } + cerr << "\n\nUSE EMPTY PIXBUF\n"; + return *empty_pixbuf; } + + Glib::RefPtr 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; - return (uint32_t) RGBA_TO_UINT(r,g,b,a); + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 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; }