fix mistaken "do not roll" conclusion in TransportFSM::compute_should_roll()
[ardour.git] / gtk2_ardour / utils.cc
index fcfdbe644cffb33871c4a16526231628e99ba0a2..e1220527999b7ef668b8dd57bd861bdd08378e6a 100644 (file)
@@ -1,62 +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 <taybin@taybin.com>
+ * Copyright (C) 2005-2009 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
+ * Copyright (C) 2006-2007 Doug McLain <doug@nostar.net>
+ * Copyright (C) 2006-2009 Sampo Savolainen <v2@iki.fi>
+ * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2007-2015 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
+ * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
+ *
+ * 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 <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
-#include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
-
 #include <cstdlib>
 #include <clocale>
 #include <cstring>
 #include <cctype>
 #include <cmath>
-#include <fstream>
 #include <list>
 #include <sys/stat.h>
-#include <gtkmm/rc.h>
-#include <gtkmm/window.h>
+
+#include <boost/algorithm/string.hpp>
+
+#include <gtk/gtkpaned.h>
 #include <gtkmm/combo.h>
 #include <gtkmm/label.h>
 #include <gtkmm/paned.h>
-#include <gtk/gtkpaned.h>
-#include <boost/algorithm/string.hpp>
+#include <gtkmm/rc.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/window.h>
 
+#include "pbd/basename.h"
 #include "pbd/file_utils.h"
+#include "pbd/stacktrace.h"
 
-#include <gtkmm2ext/utils.h>
-#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 "canvas/utils.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;
@@ -95,6 +112,38 @@ ARDOUR_UI_UTILS::just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
        return 0;
 }
 
+static bool
+idle_notify_engine_stopped ()
+{
+       Glib::RefPtr<ToggleAction> 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
@@ -208,15 +257,15 @@ ARDOUR_UI_UTILS::xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
        return (savergb);
 }
 
-/** Returns a Pango::FontDescription given a string describing the font. 
+/** 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. 
+ * 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, 
+ * 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
@@ -228,7 +277,8 @@ ARDOUR_UI_UTILS::sanitized_font (std::string const& name)
        Pango::FontDescription fd (name);
 
        if (fd.get_family().empty()) {
-               fd.set_family ("Sans");
+               /* default: "Sans" or "ArdourSans" */
+               fd.set_family (UIConfiguration::instance ().get_ui_font_family ());
        }
 
        return fd;
@@ -263,6 +313,22 @@ ARDOUR_UI_UTILS::get_font_for_style (string widgetname)
        return Pango::FontDescription (pfd); /* make a copy */
 }
 
+Gdk::Color
+ARDOUR_UI_UTILS::gdk_color_from_rgb (uint32_t rgb)
+{
+       Gdk::Color c;
+       set_color_from_rgb (c, rgb);
+       return c;
+}
+
+Gdk::Color
+ARDOUR_UI_UTILS::gdk_color_from_rgba (uint32_t rgba)
+{
+       Gdk::Color c;
+       set_color_from_rgb (c, rgba >> 8);
+       return c;
+}
+
 void
 ARDOUR_UI_UTILS::set_color_from_rgb (Gdk::Color& c, uint32_t rgb)
 {
@@ -296,33 +362,16 @@ ARDOUR_UI_UTILS::gdk_color_to_rgba (Gdk::Color const& c)
        return RGBA_TO_UINT (r,g,b,a);
 }
 
-
 bool
 ARDOUR_UI_UTILS::relay_key_press (GdkEventKey* ev, Gtk::Window* win)
 {
-       PublicEditor& ed (PublicEditor::instance());
-
-       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;
-       }
-}
-
-bool
-ARDOUR_UI_UTILS::forward_key_press (GdkEventKey* ev)
-{
-       return PublicEditor::instance().on_key_press_event(ev);
+       return ARDOUR_UI::instance()->key_event_handler (ev, win);
 }
 
 bool
-ARDOUR_UI_UTILS::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;
@@ -330,9 +379,11 @@ ARDOUR_UI_UTILS::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;
@@ -343,223 +394,9 @@ ARDOUR_UI_UTILS::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);
-}
-
-static string
-show_gdk_event_state (int state)
-{
-       string s;
-       if (state & GDK_SHIFT_MASK) {
-               s += "+SHIFT";
-       }
-       if (state & GDK_LOCK_MASK) {
-               s += "+LOCK";
-       }
-       if (state & GDK_CONTROL_MASK) {
-               s += "+CONTROL";
-       }
-       if (state & GDK_MOD1_MASK) {
-               s += "+MOD1";
-       }
-       if (state & GDK_MOD2_MASK) {
-               s += "+MOD2";
-       }
-       if (state & GDK_MOD3_MASK) {
-               s += "+MOD3";
-       }
-       if (state & GDK_MOD4_MASK) {
-               s += "+MOD4";
-       }
-       if (state & GDK_MOD5_MASK) {
-               s += "+MOD5";
-       }
-       if (state & GDK_BUTTON1_MASK) {
-               s += "+BUTTON1";
-       }
-       if (state & GDK_BUTTON2_MASK) {
-               s += "+BUTTON2";
-       }
-       if (state & GDK_BUTTON3_MASK) {
-               s += "+BUTTON3";
-       }
-       if (state & GDK_BUTTON4_MASK) {
-               s += "+BUTTON4";
-       }
-       if (state & GDK_BUTTON5_MASK) {
-               s += "+BUTTON5";
-       }
-       if (state & GDK_SUPER_MASK) {
-               s += "+SUPER";
-       }
-       if (state & GDK_HYPER_MASK) {
-               s += "+HYPER";
-       }
-       if (state & GDK_META_MASK) {
-               s += "+META";
-       }
-       if (state & GDK_RELEASE_MASK) {
-               s += "+RELEASE";
-       }
-
-       return s;
-}
-bool
-ARDOUR_UI_UTILS::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));
-        GdkModifierType modifier = GdkModifierType (ev->state);
-        modifier = GdkModifierType (modifier & gtk_accelerator_get_default_mod_mask());
-        Gtkmm2ext::possibly_translate_mod_to_make_legal_accelerator(modifier);
-
-       if (focus) {
-               if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
-                       special_handling_of_unmodified_accelerators = true;
-               }
-       }
-
-#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
-
-
-        DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 focus = %7 (%8) Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 allow_activation ? %6\n",
-                                                          win,
-                                                          ev->keyval,
-                                                         show_gdk_event_state (ev->state),
-                                                          special_handling_of_unmodified_accelerators,
-                                                          Keyboard::some_magic_widget_has_focus(),
-                                                          allow_activating,
-                                                         focus,
-                                                          (focus ? gtk_widget_get_name (focus) : "no focus widget")));
-
-       /* 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) {
-
-
-               /* 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));
-
-                       DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tmodified modifier was %1\n", show_gdk_event_state (modifier)));
-                       
-                       if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, modifier)) {
-                               DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n");
-                               return true;
-                       }
-               }
-       }
-
-       if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
-
-               /* no special handling or there are modifiers in effect: accelerate first */
-
-                DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
-               DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 name %7 string:%4 hardware_keycode:%5 group:%6\n",
-                                                                 ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group, gdk_keyval_name (ev->keyval)));
-
-               if (allow_activating) {
-                       DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
-                        if (gtk_accel_groups_activate (G_OBJECT(win), ev->keyval, modifier)) {
-                                DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
-                               return true;
-                       }
-               } else {
-                       DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
-               }
-
-                DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
-
-               return gtk_window_propagate_key_event (win, ev);
-       }
-
-       /* no modifiers, propagate first */
-
-        DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
-
-       if (!gtk_window_propagate_key_event (win, ev)) {
-                DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
-               if (allow_activating) {
-                       return gtk_accel_groups_activate (G_OBJECT(win), ev->keyval, modifier);
-               } else {
-                       DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
-               }
-
-       } else {
-                DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
-               return true;
-       }
-
-        DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
-       return true;
+       return relay_key_press(&ev, &main_window);
 }
 
 Glib::RefPtr<Gdk::Pixbuf>
@@ -579,7 +416,7 @@ ARDOUR_UI_UTILS::get_xpm (std::string name)
 
                try {
                        xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path);
-               } catch(const Glib::Error& e)   {
+               } catch (const Glib::Error& e) {
                        warning << "Caught Glib::Error: " << e.what() << endmsg;
                }
        }
@@ -587,13 +424,51 @@ ARDOUR_UI_UTILS::get_xpm (std::string name)
        return xpm_map[name];
 }
 
+void
+ARDOUR_UI_UTILS::get_color_themes (map<std::string,std::string>& themes)
+{
+       Searchpath spath(ARDOUR::theme_search_path());
+
+       for (vector<string>::iterator s = spath.begin(); s != spath.end(); ++s) {
+
+               vector<string> entries;
+
+               find_files_matching_pattern (entries, *s, string ("*") + UIConfiguration::color_file_suffix);
+
+               for (vector<string>::iterator e = entries.begin(); e != entries.end(); ++e) {
+
+                       XMLTree tree;
+
+                       tree.read ((*e).c_str());
+                       XMLNode* root = tree.root();
+
+                       if (!root || root->name() != X_("Ardour")) {
+                               continue;
+                       }
+
+                       XMLProperty const* prop = root->property (X_("theme-name"));
+
+                       if (!prop) {
+                               continue;
+                       }
+
+                       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));
+               }
+       }
+}
+
 vector<string>
 ARDOUR_UI_UTILS::get_icon_sets ()
 {
        Searchpath spath(ARDOUR::ardour_data_search_path());
        spath.add_subdirectory_to_paths ("icons");
        vector<string> r;
-       
+
        r.push_back (_("default"));
 
        for (vector<string>::iterator s = spath.begin(); s != spath.end(); ++s) {
@@ -629,22 +504,28 @@ ARDOUR_UI_UTILS::get_icon_path (const char* cname, string icon_set, bool is_imag
                /* 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);
-               
+
                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*/
@@ -717,7 +598,7 @@ ARDOUR_UI_UTILS::longest (vector<string>& strings)
 bool
 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;
@@ -799,34 +680,6 @@ ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (guint keyval)
        return false;
 }
 
-void
-ARDOUR_UI_UTILS::set_pango_fontsize ()
-{
-       long val = ARDOUR_UI::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
-ARDOUR_UI_UTILS::reset_dpi ()
-{
-       long val = ARDOUR_UI::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
 ARDOUR_UI_UTILS::resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
 {
@@ -859,20 +712,10 @@ ARDOUR_UI_UTILS::escape_underscores (string const & s)
        return o;
 }
 
-/** Replace < and > with &lt; and &gt; respectively to make < > display correctly in markup strings */
-string
-ARDOUR_UI_UTILS::escape_angled_brackets (string const & s)
-{
-       string o = s;
-       boost::replace_all (o, "<", "&lt;");
-       boost::replace_all (o, ">", "&gt;");
-       return o;
-}
-
 Gdk::Color
 ARDOUR_UI_UTILS::unique_random_color (list<Gdk::Color>& used_colors)
 {
-       Gdk::Color newcolor;
+       Gdk::Color newcolor;
 
        while (1) {
 
@@ -910,7 +753,7 @@ ARDOUR_UI_UTILS::unique_random_color (list<Gdk::Color>& used_colors)
        }
 }
 
-string 
+string
 ARDOUR_UI_UTILS::rate_as_string (float r)
 {
        char buf[32];
@@ -922,6 +765,43 @@ ARDOUR_UI_UTILS::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)
 {
@@ -958,3 +838,36 @@ ARDOUR_UI_UTILS::windows_overlap (Gtk::Window *a, Gtk::Window *b)
        }
        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 ();
+}