#include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
#include <cstdlib>
+#include <clocale>
+#include <cstring>
#include <cctype>
#include <fstream>
+#include <list>
#include <sys/stat.h>
#include <libart_lgpl/art_misc.h>
#include <gtkmm/rc.h>
#include <gtkmm/label.h>
#include <gtkmm/paned.h>
#include <gtk/gtkpaned.h>
+#include <boost/algorithm/string.hpp>
#include "pbd/file_utils.h"
#include <gtkmm2ext/utils.h>
-#include "ardour/configuration.h"
#include "ardour/rc_configuration.h"
#include "ardour/filesystem_paths.h"
#include "ardour_ui.h"
+#include "debug.h"
#include "public_editor.h"
#include "keyboard.h"
#include "utils.h"
sigc::signal<void> DPIReset;
-int
-pixel_width (const string& str, Pango::FontDescription& font)
-{
- Label foo;
- Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
-
- layout->set_font_description (font);
- layout->set_text (str);
-
- int width, height;
- Gtkmm2ext::get_ink_pixel_size (layout, width, height);
- return width;
-}
-
-string
-fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
-{
- Label foo;
- Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
- string::size_type shorter_by = 0;
- string txt;
-
- layout->set_font_description (font);
-
- actual_width = 0;
-
- string ustr = str;
- string::iterator last = ustr.end();
- --last; /* now points at final entry */
-
- txt = ustr;
-
- while (!ustr.empty()) {
-
- layout->set_text (txt);
-
- int width, height;
- Gtkmm2ext::get_ink_pixel_size (layout, width, height);
-
- if (width < pixel_width) {
- actual_width = width;
- break;
- }
-
- ustr.erase (last--);
- shorter_by++;
-
- if (with_ellipses && shorter_by > 3) {
- txt = ustr;
- txt += "...";
- } else {
- txt = ustr;
- }
- }
-
- return txt;
-}
-
-/** 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)
- */
-
-std::pair<std::string, double>
-fit_to_pixels (cairo_t* cr, std::string name, double avail)
-{
- /* XXX hopefully there exists a more efficient way of doing this */
-
- bool abbreviated = false;
- uint32_t width = 0;
-
- while (1) {
- cairo_text_extents_t ext;
- cairo_text_extents (cr, name.c_str(), &ext);
-
- if (ext.width < avail || name.length() <= 4) {
- width = ext.width;
- break;
- }
-
- if (abbreviated) {
- name = name.substr (0, name.length() - 4) + "...";
- } else {
- name = name.substr (0, name.length() - 3) + "...";
- abbreviated = true;
- }
- }
-
- return std::make_pair (name, width);
-}
-
/** Add an element to a menu, settings its sensitivity.
* @param m Menu to add to.
return new ArdourCanvas::Points (npoints);
}
-Pango::FontDescription*
+Pango::FontDescription
get_font_for_style (string widgetname)
{
Gtk::Window window (WINDOW_TOPLEVEL);
Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
- PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
+ PangoFontDescription *pfd = const_cast<PangoFontDescription *> (pango_layout_get_font_description(const_cast<PangoLayout *>(layout->gobj())));
if (!pfd) {
/* layout inherited its font description from a PangoContext */
- PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
+ PangoContext* ctxt = (PangoContext*) pango_layout_get_context (const_cast<PangoLayout*>(layout->gobj()));
pfd = pango_context_get_font_description (ctxt);
- return new Pango::FontDescription (pfd, true); /* make a copy */
+ return Pango::FontDescription (pfd); /* make a copy */
}
- return new Pango::FontDescription (pfd, true); /* make a copy */
+ return Pango::FontDescription (pfd); /* make a copy */
}
uint32_t
}
}
-
-Gdk::Color
-color_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;
- return Gdk::Color ("red");
- }
-
- 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]);
- }
-
- 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<Gdk::GC>
-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<Gdk::GC> 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<Gdk::GC> ret = Gdk::GC::create();
- ret->set_rgb_fg_color(Gdk::Color("red"));
- return ret;
-}
-
-
bool
canvas_item_visible (ArdourCanvas::Item* item)
{
return PublicEditor::instance().on_key_press_event(ev);
}
-#ifdef GTKOSX
-static guint
-osx_keyval_without_alt (guint accent_keyval)
-{
- switch (accent_keyval) {
- case GDK_oe:
- return GDK_q;
- case GDK_registered:
- return GDK_r;
- case GDK_dagger:
- return GDK_t;
- case GDK_yen:
- return GDK_y;
- case GDK_diaeresis:
- return GDK_u;
- case GDK_oslash:
- return GDK_o;
- case GDK_Greek_pi:
- return GDK_p;
- case GDK_leftdoublequotemark:
- return GDK_bracketleft;
- case GDK_leftsinglequotemark:
- return GDK_bracketright;
- case GDK_guillemotleft:
- return GDK_backslash;
- case GDK_aring:
- return GDK_a;
- case GDK_ssharp:
- return GDK_s;
- case GDK_partialderivative:
- return GDK_d;
- case GDK_function:
- return GDK_f;
- case GDK_copyright:
- return GDK_g;
- case GDK_abovedot:
- return GDK_h;
- case GDK_notsign:
- return GDK_l;
- case GDK_ellipsis:
- return GDK_semicolon;
- case GDK_ae:
- return GDK_apostrophe;
- case GDK_Greek_OMEGA:
- return GDK_z;
- case GDK_ccedilla:
- return GDK_c;
- case GDK_radical:
- return GDK_v;
- case GDK_integral:
- return GDK_b;
- case GDK_mu:
- return GDK_m;
- case GDK_lessthanequal:
- return GDK_comma;
- case GDK_greaterthanequal:
- return GDK_period;
- case GDK_division:
- return GDK_slash;
- default:
- break;
- }
-
- return GDK_VoidSymbol;
-}
-#endif
-
bool
key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
{
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));
-#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;
}
#ifdef GTKOSX
- /* should this be universally true? */
+ /* 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;
+ allow_activating = false;
}
#endif
-
-#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
+
+ 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:
all "normal text" accelerators.
*/
-#ifdef GTKOSX
if (!special_handling_of_unmodified_accelerators) {
- if (ev->state & GDK_MOD1_MASK) {
- /* we're not in a text entry or "magic focus" widget so we don't want OS X "special-character"
- text-style handling of alt-<key>. change the keyval back to what it would be without
- the alt key. this way, we see <alt>-v rather than <alt>-radical and so on.
- */
- guint keyval_without_alt = osx_keyval_without_alt (ev->keyval);
-
- if (keyval_without_alt != GDK_VoidSymbol) {
-#ifdef DEBUG_ACCELERATOR_HANDLING
- cerr << "Remapped " << gdk_keyval_name (ev->keyval) << " to " << gdk_keyval_name (keyval_without_alt) << endl;
-#endif ev->keyval = keyval_without_alt;
- }
- }
- }
-#endif
-
- 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.
+ 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)) {
- if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
+ 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;
}
}
}
- /* 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
+ DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
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");
}
-#ifdef DEBUG_ACCELERATOR_HANDLING
- if (debug) {
- cerr << "\tnot accelerated, now propagate\n";
- }
-#endif
+ DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
+
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
+ 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 {
-#ifdef DEBUG_ACCELERATOR_HANDLING
- if (debug) {
- cerr << "\thandled by propagate\n";
- }
-#endif
+ DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
return true;
}
-#ifdef DEBUG_ACCELERATOR_HANDLING
- if (debug) {
- cerr << "\tnot handled\n";
- }
-#endif
+ DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
return true;
}
{
if (!xpm_map[name]) {
- SearchPath spath(ARDOUR::ardour_search_path());
- spath += ARDOUR::system_data_search_path();
+ SearchPath spath(ARDOUR::ardour_data_search_path());
spath.add_subdirectory_to_paths("pixmaps");
- sys::path data_file_path;
+ std::string 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;
}
try {
- xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
+ xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path);
} catch(const Glib::Error& e) {
warning << "Caught Glib::Error: " << e.what() << endmsg;
}
string name = cname;
name += X_(".png");
- SearchPath spath(ARDOUR::ardour_search_path());
- spath += ARDOUR::system_data_search_path();
+ SearchPath spath(ARDOUR::ardour_data_search_path());
spath.add_subdirectory_to_paths("icons");
- sys::path data_file_path;
+ std::string 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;
+ fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
}
- return data_file_path.to_string();
+ return data_file_path;
}
Glib::RefPtr<Gdk::Pixbuf>
} catch (const Gdk::PixbufError &e) {
cerr << "Caught PixbufError: " << e.what() << endl;
} catch (...) {
- g_message("Caught ... ");
+ error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg;
}
return img;
bool
key_is_legal_for_numeric_entry (guint keyval)
{
+ /* we assume that this does not change over the life of the process
+ */
+
+ static int comma_decimal = -1;
+
switch (keyval) {
- case GDK_minus:
- case GDK_plus:
case GDK_period:
case GDK_comma:
+ if (comma_decimal < 0) {
+ std::lconv* lc = std::localeconv();
+ if (strchr (lc->decimal_point, ',') != 0) {
+ comma_decimal = 1;
+ } else {
+ comma_decimal = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (keyval) {
+ case GDK_decimalpoint:
+ case GDK_KP_Separator:
+ return true;
+
+ case GDK_period:
+ if (comma_decimal) {
+ return false;
+ } else {
+ return true;
+ }
+ break;
+ case GDK_comma:
+ if (comma_decimal) {
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case GDK_minus:
+ case GDK_plus:
case GDK_0:
case GDK_1:
case GDK_2:
/* FT2 rendering - used by GnomeCanvas, sigh */
- pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
+ pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_new(), val/1024, val/1024);
/* Cairo rendering, in case there is any */
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<Gdk::Color>& used_colors)
+{
+ Gdk::Color newcolor;
+
+ while (1) {
+
+ /* avoid neon/glowing tones by limiting them to the
+ "inner section" (paler) of a color wheel/circle.
+ */
+
+ const int32_t max_saturation = 48000; // 65535 would open up the whole color wheel
+
+ newcolor.set_red (random() % max_saturation);
+ newcolor.set_blue (random() % max_saturation);
+ newcolor.set_green (random() % max_saturation);
+
+ if (used_colors.size() == 0) {
+ used_colors.push_back (newcolor);
+ return newcolor;
+ }
+
+ for (list<Gdk::Color>::iterator i = used_colors.begin(); i != used_colors.end(); ++i) {
+ Gdk::Color c = *i;
+ float rdelta, bdelta, gdelta;
+
+ rdelta = newcolor.get_red() - c.get_red();
+ bdelta = newcolor.get_blue() - c.get_blue();
+ gdelta = newcolor.get_green() - c.get_green();
+
+ if (sqrt (rdelta*rdelta + bdelta*bdelta + gdelta*gdelta) > 25.0) {
+ used_colors.push_back (newcolor);
+ return newcolor;
+ }
+ }
+
+ /* XXX need throttle here to make sure we don't spin for ever */
+ }
+}