+/** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
+string
+escape_underscores (string const & s)
+{
+ string o;
+ string::size_type const N = s.length ();
+
+ for (string::size_type i = 0; i < N; ++i) {
+ if (s[i] == '_') {
+ o += "__";
+ } else {
+ o += s[i];
+ }
+ }
+
+ 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 */
+ }
+}