X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Futils.cc;h=e1220527999b7ef668b8dd57bd861bdd08378e6a;hb=refs%2Fheads%2Fcarl-master;hp=777eb6261650bc3c42eec0b58501b343b3e65520;hpb=2e27e21d3a09889311e18a8efe11abcaa6d9c8b3;p=ardour.git diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 777eb62616..e122052799 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -1,60 +1,79 @@ /* - 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ + * Copyright (C) 2005-2006 Taybin Rutkin + * Copyright (C) 2005-2009 Nick Mainsbridge + * Copyright (C) 2005-2018 Paul Davis + * Copyright (C) 2005 Karsten Wiese + * Copyright (C) 2006-2007 Doug McLain + * Copyright (C) 2006-2009 Sampo Savolainen + * Copyright (C) 2007-2015 David Robillard + * Copyright (C) 2007-2015 Tim Mayberry + * Copyright (C) 2009-2012 Carl Hetherington + * Copyright (C) 2013-2019 Robin Gareus + * Copyright (C) 2013 John Emmas + * Copyright (C) 2015 André Nusser + * + * This program is free software; you can 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #ifdef WAF_BUILD #include "gtk2ardour-config.h" #endif -#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 #include #include #include -#include -#include +#include +#include +#include +#include "pbd/basename.h" #include "pbd/file_utils.h" +#include "pbd/stacktrace.h" -#include -#include "ardour/rc_configuration.h" +#include "ardour/audioengine.h" #include "ardour/filesystem_paths.h" +#include "ardour/search_paths.h" + +#include "gtkmm2ext/colors.h" +#include "gtkmm2ext/utils.h" + #include "canvas/item.h" -#include "ardour_ui.h" +#include "actions.h" +#include "context_menu_helper.h" #include "debug.h" #include "public_editor.h" #include "keyboard.h" #include "utils.h" -#include "i18n.h" +#include "pbd/i18n.h" #include "rgb_macros.h" #include "gui_thread.h" +#include "ui_config.h" +#include "ardour_dialog.h" +#include "ardour_ui.h" using namespace std; using namespace Gtk; @@ -62,7 +81,13 @@ using namespace Glib; using namespace PBD; using Gtkmm2ext::Keyboard; -sigc::signal DPIReset; +namespace ARDOUR_UI_UTILS { + sigc::signal DPIReset; +} + +#ifdef PLATFORM_WINDOWS +#define random() rand() +#endif /** Add an element to a menu, settings its sensitivity. @@ -71,7 +96,7 @@ sigc::signal DPIReset; * @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) +ARDOUR_UI_UTILS::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s) { m.push_back (e); if (!s) { @@ -81,12 +106,44 @@ add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, gint -just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win) +ARDOUR_UI_UTILS::just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win) { win->hide (); return 0; } +static bool +idle_notify_engine_stopped () +{ + Glib::RefPtr tact = ActionManager::get_toggle_action ("Window", "toggle-audio-midi-setup"); + + MessageDialog msg ( + _("The current operation is not possible because of an error communicating with the audio hardware."), + false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); + + msg.add_button (_("Cancel"), Gtk::RESPONSE_CANCEL); + + if (tact && !tact->get_active()) { + msg.add_button (_("Configure Hardware"), Gtk::RESPONSE_OK); + } + + if (msg.run () == Gtk::RESPONSE_OK) { + tact->set_active (); + } + return false; /* do not call again */ +} + +bool +ARDOUR_UI_UTILS::engine_is_running () +{ + if (ARDOUR::AudioEngine::instance()->running ()) { + return true; + } + Glib::signal_idle().connect (sigc::ptr_fun (&idle_notify_engine_stopped)); + return false; +} + + /* xpm2rgb copied from nixieclock, which bore the legend: nixieclock - a nixie desktop timepiece @@ -96,7 +153,7 @@ just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win) */ unsigned char* -xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h) +ARDOUR_UI_UTILS::xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h) { static long vals[256], val; uint32_t t, x, y, colors, cpp; @@ -138,7 +195,7 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h) } unsigned char* -xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h) +ARDOUR_UI_UTILS::xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h) { static long vals[256], val; uint32_t t, x, y, colors, cpp; @@ -200,8 +257,35 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h) return (savergb); } +/** Returns a Pango::FontDescription given a string describing the font. + * + * If the returned FontDescription does not specify a family, then + * the family is set to "Sans". This mirrors GTK's behaviour in + * gtkstyle.c. + * + * Some environments will force Pango to specify the family + * even if it was not specified in the string describing the font. + * Such environments should be left unaffected by this function, + * since the font family will be left alone. + * + * There may be other similar font specification enforcement + * that we might add here later. + */ Pango::FontDescription -get_font_for_style (string widgetname) +ARDOUR_UI_UTILS::sanitized_font (std::string const& name) +{ + Pango::FontDescription fd (name); + + if (fd.get_family().empty()) { + /* default: "Sans" or "ArdourSans" */ + fd.set_family (UIConfiguration::instance ().get_ui_font_family ()); + } + + return fd; +} + +Pango::FontDescription +ARDOUR_UI_UTILS::get_font_for_style (string widgetname) { Gtk::Window window (WINDOW_TOPLEVEL); Gtk::Label foobar; @@ -229,144 +313,65 @@ get_font_for_style (string widgetname) return Pango::FontDescription (pfd); /* 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) +Gdk::Color +ARDOUR_UI_UTILS::gdk_color_from_rgb (uint32_t rgb) { - /* 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* rc = foo.get_style()->gobj()->rc_style; - - if (rc) { - if (attr == "fg") { - r = rc->fg[state].red / 257; - g = rc->fg[state].green / 257; - b = rc->fg[state].blue / 257; - - /* what a hack ... "a" is for "active" */ - if (state == Gtk::STATE_NORMAL && rgba) { - a = rc->fg[GTK_STATE_ACTIVE].red / 257; - } - } else if (attr == "bg") { - r = g = b = 0; - r = rc->bg[state].red / 257; - g = rc->bg[state].green / 257; - b = rc->bg[state].blue / 257; - } else if (attr == "base") { - r = rc->base[state].red / 257; - g = rc->base[state].green / 257; - b = rc->base[state].blue / 257; - } else if (attr == "text") { - r = rc->text[state].red / 257; - g = rc->text[state].green / 257; - b = rc->text[state].blue / 257; - } - } else { - 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); - } + Gdk::Color c; + set_color_from_rgb (c, rgb); + return c; } -bool -rgba_p_from_style (string style, float *r, float *g, float *b, string attr, int state) +Gdk::Color +ARDOUR_UI_UTILS::gdk_color_from_rgba (uint32_t rgba) { - static Gtk::Window* window = 0; - assert (r && g && b); - - if (window == 0) { - window = new Window (WINDOW_TOPLEVEL); - } - - Gtk::EventBox foo; - - window->add (foo); - - foo.set_name (style); - foo.ensure_style (); - - GtkRcStyle* rc = foo.get_style()->gobj()->rc_style; - - if (!rc) { - warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl; - return false; - } - if (attr == "fg") { - *r = rc->fg[state].red / 65535.0; - *g = rc->fg[state].green / 65535.0; - *b = rc->fg[state].blue / 65535.0; - } else if (attr == "bg") { - *r = rc->bg[state].red / 65535.0; - *g = rc->bg[state].green / 65535.0; - *b = rc->bg[state].blue / 65535.0; - } else if (attr == "base") { - *r = rc->base[state].red / 65535.0; - *g = rc->base[state].green / 65535.0; - *b = rc->base[state].blue / 65535.0; - } else if (attr == "text") { - *r = rc->text[state].red / 65535.0; - *g = rc->text[state].green / 65535.0; - *b = rc->text[state].blue / 65535.0; - } else { - return false; - } + Gdk::Color c; + set_color_from_rgb (c, rgba >> 8); + return c; +} - window->remove (); - return true; +void +ARDOUR_UI_UTILS::set_color_from_rgb (Gdk::Color& c, uint32_t rgb) +{ + /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by + multiplying by 256. + */ + c.set_rgb ((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256); } void -set_color (Gdk::Color& c, int rgb) +ARDOUR_UI_UTILS::set_color_from_rgba (Gdk::Color& c, uint32_t rgba) { - c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256); + /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by + multiplying by 256. + */ + c.set_rgb ((rgba >> 24)*256, ((rgba & 0xff0000) >> 16)*256, ((rgba & 0xff00) >> 8)*256); } -bool -relay_key_press (GdkEventKey* ev, Gtk::Window* win) +uint32_t +ARDOUR_UI_UTILS::gdk_color_to_rgba (Gdk::Color const& c) { - PublicEditor& ed (PublicEditor::instance()); + /* since alpha value is not available from a Gdk::Color, it is + hardcoded as 0xff (aka 255 or 1.0) + */ - if (!key_press_focus_accelerator_handler (*win, ev)) { - if (&ed == 0) { - /* early key press in pre-main-window-dialogs, no editor yet */ - return false; - } - return ed.on_key_press_event(ev); - } else { - return true; - } + const uint32_t r = c.get_red_p () * 255.0; + const uint32_t g = c.get_green_p () * 255.0; + const uint32_t b = c.get_blue_p () * 255.0; + const uint32_t a = 0xff; + + return RGBA_TO_UINT (r,g,b,a); } bool -forward_key_press (GdkEventKey* ev) +ARDOUR_UI_UTILS::relay_key_press (GdkEventKey* ev, Gtk::Window* win) { - return PublicEditor::instance().on_key_press_event(ev); + return ARDOUR_UI::instance()->key_event_handler (ev, win); } bool -emulate_key_event (Gtk::Widget* w, unsigned int keyval) +ARDOUR_UI_UTILS::emulate_key_event (unsigned int keyval) { - GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET(w->gobj())); + GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET(ARDOUR_UI::instance()->main_window().gobj())); GdkKeymap *keymap = gdk_keymap_get_for_display (display); GdkKeymapKey *keymapkey = NULL; gint n_keys; @@ -374,9 +379,11 @@ emulate_key_event (Gtk::Widget* w, unsigned int keyval) if (!gdk_keymap_get_entries_for_keyval(keymap, keyval, &keymapkey, &n_keys)) return false; if (n_keys !=1) { g_free(keymapkey); return false;} + Gtk::Window& main_window (ARDOUR_UI::instance()->main_window()); + GdkEventKey ev; ev.type = GDK_KEY_PRESS; - ev.window = gtk_widget_get_window(GTK_WIDGET(w->gobj())); + ev.window = main_window.get_window()->gobj(); ev.send_event = FALSE; ev.time = 0; ev.state = 0; @@ -387,226 +394,163 @@ emulate_key_event (Gtk::Widget* w, unsigned int keyval) ev.group = keymapkey[0].group; g_free(keymapkey); - forward_key_press(&ev); + relay_key_press (&ev, &main_window); ev.type = GDK_KEY_RELEASE; - return forward_key_press(&ev); + return relay_key_press(&ev, &main_window); } -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; - /* consider all relevant modifiers but not LOCK or SHIFT */ - const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK)); - - if (focus) { - if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) { - special_handling_of_unmodified_accelerators = true; - } - } +Glib::RefPtr +ARDOUR_UI_UTILS::get_xpm (std::string name) +{ + if (!xpm_map[name]) { -#ifdef GTKOSX - /* at one time this appeared to be necessary. As of July 2012, it does not - appear to be. if it ever is necessar, figure out if it should apply - to all platforms. - */ -#if 0 - if (Keyboard::some_magic_widget_has_focus ()) { - allow_activating = false; - } -#endif -#endif + Searchpath spath(ARDOUR::ardour_data_search_path()); + spath.add_subdirectory_to_paths("pixmaps"); - DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 focus = %7 Key event: code = %2 state = %3 special handling ? %4 magic widget focus ? %5 allow_activation ? %6\n", - win, - ev->keyval, - ev->state, - special_handling_of_unmodified_accelerators, - Keyboard::some_magic_widget_has_focus(), - allow_activating, - focus)); - - /* 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. - */ + std::string data_file_path; - if (!special_handling_of_unmodified_accelerators) { - - /* XXX note that for a brief moment, the conditional above - * included "|| (ev->state & mask)" so as to enforce the - * implication of special_handling_of_UNMODIFIED_accelerators. - * however, this forces any key that GTK doesn't allow and that - * we have an alternative (see next comment) for to be - * automatically sent through the accel groups activation - * pathway, which prevents individual widgets & canvas items - * from ever seeing it if is used by a key binding. - * - * specifically, this hid Ctrl-down-arrow from MIDI region - * views because it is also bound to an action. - * - * until we have a robust, clean binding system, this - * quirk will have to remain in place. - */ - - /* pretend that certain key events that GTK does not allow - to be used as accelerators are actually something that - it does allow. but only where there are no modifiers. - */ - - uint32_t fakekey = ev->keyval; - - if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) { - DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tactivate (was %1 now %2) without special hanlding of unmodified accels\n", - ev->keyval, fakekey)); - - GdkModifierType mod = GdkModifierType (ev->state); - - mod = GdkModifierType (mod & gtk_accelerator_get_default_mod_mask()); -#ifdef GTKOSX - /* GTK on OS X is currently (February 2012) setting both - the Meta and Mod2 bits in the event modifier state if - the Command key is down. - - gtk_accel_groups_activate() does not invoke any of the logic - that gtk_window_activate_key() will that sorts out that stupid - state of affairs, and as a result it fails to find a match - for the key event and the current set of accelerators. - - to fix this, if the meta bit is set, remove the mod2 bit - from the modifier. this assumes that our bindings use Primary - which will have set the meta bit in the accelerator entry. - */ - if (mod & GDK_META_MASK) { - mod = GdkModifierType (mod & ~GDK_MOD2_MASK); - } -#endif + if(!find_file (spath, name, data_file_path)) { + fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg; + } - if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, mod)) { - DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n"); - return true; - } + try { + xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path); + } catch (const Glib::Error& e) { + warning << "Caught Glib::Error: " << e.what() << endmsg; } } - if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) { + return xpm_map[name]; +} - /* no special handling or there are modifiers in effect: accelerate first */ +void +ARDOUR_UI_UTILS::get_color_themes (map& themes) +{ + Searchpath spath(ARDOUR::theme_search_path()); - DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n"); - DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 string:%4 hardware_keycode:%5 group:%6\n", - ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group)); + for (vector::iterator s = spath.begin(); s != spath.end(); ++s) { - if (allow_activating) { - DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n"); - if (gtk_window_activate_key (win, ev)) { - DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n"); - return true; - } - } else { - DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n"); - } + vector entries; - DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n"); + find_files_matching_pattern (entries, *s, string ("*") + UIConfiguration::color_file_suffix); - return gtk_window_propagate_key_event (win, ev); - } + for (vector::iterator e = entries.begin(); e != entries.end(); ++e) { - /* no modifiers, propagate first */ + XMLTree tree; - DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n"); + tree.read ((*e).c_str()); + XMLNode* root = tree.root(); - if (!gtk_window_propagate_key_event (win, ev)) { - DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n"); - if (allow_activating) { - return gtk_window_activate_key (win, ev); - } else { - DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n"); - } + if (!root || root->name() != X_("Ardour")) { + continue; + } - } else { - DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n"); - return true; - } + XMLProperty const* prop = root->property (X_("theme-name")); + + if (!prop) { + continue; + } - DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n"); - return true; + std::string color_name = basename_nosuffix(*e); + size_t sep = color_name.find_first_of("-"); + if (sep != string::npos) { + color_name = color_name.substr (0, sep); + } + themes.insert (make_pair (prop->value(), color_name)); + } + } } -Glib::RefPtr -get_xpm (std::string name) +vector +ARDOUR_UI_UTILS::get_icon_sets () { - if (!xpm_map[name]) { + Searchpath spath(ARDOUR::ardour_data_search_path()); + spath.add_subdirectory_to_paths ("icons"); + vector r; - Searchpath spath(ARDOUR::ardour_data_search_path()); + r.push_back (_("default")); - spath.add_subdirectory_to_paths("pixmaps"); + for (vector::iterator s = spath.begin(); s != spath.end(); ++s) { - std::string data_file_path; + vector entries; - if(!find_file_in_search_path (spath, name, data_file_path)) { - fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg; - } + get_paths (entries, *s, false, false); - try { - xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path); - } catch(const Glib::Error& e) { - warning << "Caught Glib::Error: " << e.what() << endmsg; + for (vector::iterator e = entries.begin(); e != entries.end(); ++e) { + if (Glib::file_test (*e, Glib::FILE_TEST_IS_DIR)) { + r.push_back (Glib::filename_to_utf8 (Glib::path_get_basename(*e))); + } } } - return xpm_map[name]; + return r; } std::string -get_icon_path (const char* cname) +ARDOUR_UI_UTILS::get_icon_path (const char* cname, string icon_set, bool is_image) { + std::string data_file_path; string name = cname; - name += X_(".png"); + + if (is_image) { + name += X_(".png"); + } Searchpath spath(ARDOUR::ardour_data_search_path()); - spath.add_subdirectory_to_paths("icons"); + if (!icon_set.empty() && icon_set != _("default")) { - std::string data_file_path; + /* add "icons/icon_set" but .. not allowed to add both of these at once */ + spath.add_subdirectory_to_paths ("icons"); + spath.add_subdirectory_to_paths (icon_set); - if (!find_file_in_search_path (spath, name, data_file_path)) { - fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg; + find_file (spath, name, data_file_path); + } else { + spath.add_subdirectory_to_paths ("icons"); + find_file (spath, name, data_file_path); + } + + if (data_file_path.empty()) { + Searchpath rc (ARDOUR::ardour_data_search_path()); + rc.add_subdirectory_to_paths ("resources"); + find_file (rc, name, data_file_path); + } + + if (is_image && data_file_path.empty()) { + + if (!icon_set.empty() && icon_set != _("default")) { + warning << string_compose (_("icon \"%1\" not found for icon set \"%2\", fallback to default"), cname, icon_set) << endmsg; + } + + Searchpath def (ARDOUR::ardour_data_search_path()); + def.add_subdirectory_to_paths ("icons"); + + if (!find_file (def, name, data_file_path)) { + fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg; + abort(); /*NOTREACHED*/ + } } return data_file_path; } +Glib::RefPtr +ARDOUR_UI_UTILS::get_icon (const char* cname, string icon_set) +{ + Glib::RefPtr img; + try { + img = Gdk::Pixbuf::create_from_file (get_icon_path (cname, icon_set)); + } catch (const Gdk::PixbufError &e) { + cerr << "Caught PixbufError: " << e.what() << endl; + } catch (...) { + error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg; + } + + return img; +} + +namespace ARDOUR_UI_UTILS { Glib::RefPtr get_icon (const char* cname) { @@ -621,9 +565,10 @@ get_icon (const char* cname) return img; } +} string -longest (vector& strings) +ARDOUR_UI_UTILS::longest (vector& strings) { if (strings.empty()) { return string (""); @@ -651,9 +596,9 @@ longest (vector& strings) } bool -key_is_legal_for_numeric_entry (guint keyval) +ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (guint keyval) { - /* we assume that this does not change over the life of the process + /* we assume that this does not change over the life of the process */ static int comma_decimal = -1; @@ -734,36 +679,9 @@ key_is_legal_for_numeric_entry (guint keyval) return false; } -void -set_pango_fontsize () -{ - long val = ARDOUR::Config->get_font_scale(); - - /* FT2 rendering - used by GnomeCanvas, sigh */ - -#ifndef PLATFORM_WINDOWS - pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_new(), val/1024, val/1024); -#endif - - /* Cairo rendering, in case there is any */ - - pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024); -} void -reset_dpi () -{ - long val = ARDOUR::Config->get_font_scale(); - set_pango_fontsize (); - /* Xft rendering */ - - gtk_settings_set_long_property (gtk_settings_get_default(), - "gtk-xft-dpi", val, "ardour"); - DPIReset();//Emit Signal -} - -void -resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height) +ARDOUR_UI_UTILS::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; @@ -778,7 +696,7 @@ resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */ string -escape_underscores (string const & s) +ARDOUR_UI_UTILS::escape_underscores (string const & s) { string o; string::size_type const N = s.length (); @@ -794,20 +712,10 @@ escape_underscores (string const & s) return o; } -/** Replace < and > with < and > respectively to make < > display correctly in markup strings */ -string -escape_angled_brackets (string const & s) -{ - string o = s; - boost::replace_all (o, "<", "<"); - boost::replace_all (o, ">", ">"); - return o; -} - Gdk::Color -unique_random_color (list& used_colors) +ARDOUR_UI_UTILS::unique_random_color (list& used_colors) { - Gdk::Color newcolor; + Gdk::Color newcolor; while (1) { @@ -845,8 +753,8 @@ unique_random_color (list& used_colors) } } -string -rate_as_string (float r) +string +ARDOUR_UI_UTILS::rate_as_string (float r) { char buf[32]; if (fmod (r, 1000.0f)) { @@ -856,3 +764,110 @@ rate_as_string (float r) } return buf; } + +string +ARDOUR_UI_UTILS::samples_as_time_string (samplecnt_t s, float rate, bool show_samples) +{ + char buf[32]; + if (rate <= 0) { + snprintf (buf, sizeof (buf), "--"); + } else if (s == 0) { + snprintf (buf, sizeof (buf), "0"); + } else if (s < 1000 && show_samples) { + /* 0 .. 999 spl */ + snprintf (buf, sizeof (buf), "%" PRId64" spl", s); + } else if (s < (rate / 1000.f)) { + /* 0 .. 999 usec */ + snprintf (buf, sizeof (buf), "%.0f \u00B5s", s * 1e+6f / rate); + } else if (s < (rate / 100.f)) { + /* 1.000 .. 9.999 ms */ + snprintf (buf, sizeof (buf), "%.3f ms", s * 1e+3f / rate); + } else if (s < (rate / 10.f)) { + /* 1.00 .. 99.99 ms */ + snprintf (buf, sizeof (buf), "%.2f ms", s * 1e+3f / rate); + } else if (s < rate) { + /* 100.0 .. 999.9 ms */ + snprintf (buf, sizeof (buf), "%.1f ms", s * 1e+3f / rate); + } else if (s < rate * 10.f) { + /* 1.000 s .. 9.999 s */ + snprintf (buf, sizeof (buf), "%.3f s", s / rate); + } else if (s < rate * 90.f) { + /* 10.00 s .. 89.99 s */ + snprintf (buf, sizeof (buf), "%.2f s", s / rate); + } else { + /* 1m30.0 ... */ + snprintf (buf, sizeof (buf), "'%.0fm%.1f", s / (60.f * rate), fmodf (s / rate, 60)); + } + buf[31] = '\0'; + return buf; +} + +bool +ARDOUR_UI_UTILS::windows_overlap (Gtk::Window *a, Gtk::Window *b) +{ + + if (!a || !b) { + return false; + } + if (a->get_screen() == b->get_screen()) { + gint ex, ey, ew, eh; + gint mx, my, mw, mh; + + a->get_position (ex, ey); + a->get_size (ew, eh); + b->get_position (mx, my); + b->get_size (mw, mh); + + GdkRectangle e; + GdkRectangle m; + GdkRectangle r; + + e.x = ex; + e.y = ey; + e.width = ew; + e.height = eh; + + m.x = mx; + m.y = my; + m.width = mw; + m.height = mh; + + if (gdk_rectangle_intersect (&e, &m, &r)) { + return true; + } + } + return false; +} + +bool +ARDOUR_UI_UTILS::overwrite_file_dialog (Gtk::Window& parent, string title, string text) +{ + ArdourDialog dialog (parent, title, true); + Label label (text); + + dialog.get_vbox()->pack_start (label, true, true); + dialog.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog.add_button (_("Overwrite"), Gtk::RESPONSE_ACCEPT); + dialog.show_all (); + + switch (dialog.run()) { + case RESPONSE_ACCEPT: + return true; + case RESPONSE_CANCEL: + default: + return false; + } +} + +bool +ARDOUR_UI_UTILS::running_from_source_tree () +{ + gchar const *x = g_getenv ("ARDOUR_THEMES_PATH"); + return x && (string (x).find ("gtk2_ardour") != string::npos); +} + +Gtk::Menu* +ARDOUR_UI_UTILS::shared_popup_menu () +{ + return ARDOUR_UI::instance()->shared_popup_menu (); +}