#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 "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;
return 0;
}
+static bool
+idle_notify_engine_stopped ()
+{
+ Glib::RefPtr<Action> act = ActionManager::get_action ("Window", "toggle-audio-midi-setup");
+ Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
+
+ 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
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
return Pango::FontDescription (pfd); /* make a copy */
}
-uint32_t
-ARDOUR_UI_UTILS::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) RGBA_TO_UINT(r,g,b,255);
- }
+ Gdk::Color c;
+ set_color_from_rgb (c, rgb);
+ return c;
}
-bool
-ARDOUR_UI_UTILS::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;
- }
-
- window->remove ();
- return true;
+ Gdk::Color c;
+ set_color_from_rgb (c, rgba >> 8);
+ return c;
}
void
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;
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;
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);
-}
-
-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));
-
- 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 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.
- */
-
- 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 (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, mod)) {
- 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_window_activate_key (win, ev)) {
- 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_window_activate_key (win, ev);
- } 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>
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) {
}
std::string
-ARDOUR_UI_UTILS::get_icon_path (const char* cname, string icon_set)
+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());
/* 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;
- /*NOTREACHED*/
+ abort(); /*NOTREACHED*/
}
}
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;
return false;
}
-void
-ARDOUR_UI_UTILS::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
-ARDOUR_UI_UTILS::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
ARDOUR_UI_UTILS::resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
{
return o;
}
-/** Replace < and > with < and > 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, "<", "<");
- boost::replace_all (o, ">", ">");
- return o;
-}
-
Gdk::Color
ARDOUR_UI_UTILS::unique_random_color (list<Gdk::Color>& used_colors)
{
- Gdk::Color newcolor;
+ Gdk::Color newcolor;
while (1) {
}
}
-string
+string
ARDOUR_UI_UTILS::rate_as_string (float r)
{
char buf[32];
}
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);
+}