mingw build fixes (tested with i686-w64-mingw32 on linux-x86_64)
[ardour.git] / gtk2_ardour / utils.cc
1 /*
2     Copyright (C) 2003 Paul Davis
3
4     This program is free software; you an redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
25 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
26
27 #include <cstdlib>
28 #include <clocale>
29 #include <cstring>
30 #include <cctype>
31 #include <cmath>
32 #include <fstream>
33 #include <list>
34 #include <sys/stat.h>
35 #include <gtkmm/rc.h>
36 #include <gtkmm/window.h>
37 #include <gtkmm/combo.h>
38 #include <gtkmm/label.h>
39 #include <gtkmm/paned.h>
40 #include <gtk/gtkpaned.h>
41 #include <boost/algorithm/string.hpp>
42
43 #include "pbd/file_utils.h"
44
45 #include <gtkmm2ext/utils.h>
46 #include "ardour/rc_configuration.h"
47 #include "ardour/filesystem_paths.h"
48 #include "canvas/item.h"
49
50 #include "ardour_ui.h"
51 #include "debug.h"
52 #include "public_editor.h"
53 #include "keyboard.h"
54 #include "utils.h"
55 #include "i18n.h"
56 #include "rgb_macros.h"
57 #include "gui_thread.h"
58
59 using namespace std;
60 using namespace Gtk;
61 using namespace Glib;
62 using namespace PBD;
63 using Gtkmm2ext::Keyboard;
64
65 sigc::signal<void>  DPIReset;
66
67 #ifdef PLATFORM_WINDOWS
68 #define random() rand()
69 #endif
70
71
72 /** Add an element to a menu, settings its sensitivity.
73  * @param m Menu to add to.
74  * @param e Element to add.
75  * @param s true to make sensitive, false to make insensitive
76  */
77 void
78 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
79 {
80         m.push_back (e);
81         if (!s) {
82                 m.back().set_sensitive (false);
83         }
84 }
85
86
87 gint
88 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
89 {
90         win->hide ();
91         return 0;
92 }
93
94 /* xpm2rgb copied from nixieclock, which bore the legend:
95
96     nixieclock - a nixie desktop timepiece
97     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
98
99     and was released under the GPL.
100 */
101
102 unsigned char*
103 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
104 {
105         static long vals[256], val;
106         uint32_t t, x, y, colors, cpp;
107         unsigned char c;
108         unsigned char *savergb, *rgb;
109
110         // PARSE HEADER
111
112         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
113                 error << string_compose (_("bad XPM header %1"), xpm[0])
114                       << endmsg;
115                 return 0;
116         }
117
118         savergb = rgb = (unsigned char*) malloc (h * w * 3);
119
120         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
121         for (t = 0; t < colors; ++t) {
122                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
123                 vals[c] = val;
124         }
125
126         // COLORMAP -> RGB CONVERSION
127         //    Get low 3 bytes from vals[]
128         //
129
130         const char *p;
131         for (y = h-1; y > 0; --y) {
132
133                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
134                         val = vals[(int)*p++];
135                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
136                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
137                         *(rgb+0) = val & 0xff;             // 0:R
138                 }
139         }
140
141         return (savergb);
142 }
143
144 unsigned char*
145 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
146 {
147         static long vals[256], val;
148         uint32_t t, x, y, colors, cpp;
149         unsigned char c;
150         unsigned char *savergb, *rgb;
151         char transparent;
152
153         // PARSE HEADER
154
155         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
156                 error << string_compose (_("bad XPM header %1"), xpm[0])
157                       << endmsg;
158                 return 0;
159         }
160
161         savergb = rgb = (unsigned char*) malloc (h * w * 4);
162
163         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
164
165         if (strstr (xpm[1], "None")) {
166                 sscanf (xpm[1], "%c", &transparent);
167                 t = 1;
168         } else {
169                 transparent = 0;
170                 t = 0;
171         }
172
173         for (; t < colors; ++t) {
174                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
175                 vals[c] = val;
176         }
177
178         // COLORMAP -> RGB CONVERSION
179         //    Get low 3 bytes from vals[]
180         //
181
182         const char *p;
183         for (y = h-1; y > 0; --y) {
184
185                 char alpha;
186
187                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
188
189                         if (transparent && (*p++ == transparent)) {
190                                 alpha = 0;
191                                 val = 0;
192                         } else {
193                                 alpha = 255;
194                                 val = vals[(int)*p];
195                         }
196
197                         *(rgb+3) = alpha;                  // 3: alpha
198                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
199                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
200                         *(rgb+0) = val & 0xff;             // 0:R
201                 }
202         }
203
204         return (savergb);
205 }
206
207 Pango::FontDescription
208 get_font_for_style (string widgetname)
209 {
210         Gtk::Window window (WINDOW_TOPLEVEL);
211         Gtk::Label foobar;
212         Glib::RefPtr<Gtk::Style> style;
213
214         window.add (foobar);
215         foobar.set_name (widgetname);
216         foobar.ensure_style();
217
218         style = foobar.get_style ();
219
220         Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
221
222         PangoFontDescription *pfd = const_cast<PangoFontDescription *> (pango_layout_get_font_description(const_cast<PangoLayout *>(layout->gobj())));
223
224         if (!pfd) {
225
226                 /* layout inherited its font description from a PangoContext */
227
228                 PangoContext* ctxt = (PangoContext*) pango_layout_get_context (const_cast<PangoLayout*>(layout->gobj()));
229                 pfd =  pango_context_get_font_description (ctxt);
230                 return Pango::FontDescription (pfd); /* make a copy */
231         }
232
233         return Pango::FontDescription (pfd); /* make a copy */
234 }
235
236 uint32_t
237 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
238 {
239         /* In GTK+2, styles aren't set up correctly if the widget is not
240            attached to a toplevel window that has a screen pointer.
241         */
242
243         static Gtk::Window* window = 0;
244
245         if (window == 0) {
246                 window = new Window (WINDOW_TOPLEVEL);
247         }
248
249         Gtk::Label foo;
250
251         window->add (foo);
252
253         foo.set_name (style);
254         foo.ensure_style ();
255
256         GtkRcStyle* rc = foo.get_style()->gobj()->rc_style;
257
258         if (rc) {
259                 if (attr == "fg") {
260                         r = rc->fg[state].red / 257;
261                         g = rc->fg[state].green / 257;
262                         b = rc->fg[state].blue / 257;
263
264                         /* what a hack ... "a" is for "active" */
265                         if (state == Gtk::STATE_NORMAL && rgba) {
266                                 a = rc->fg[GTK_STATE_ACTIVE].red / 257;
267                         }
268                 } else if (attr == "bg") {
269                         r = g = b = 0;
270                         r = rc->bg[state].red / 257;
271                         g = rc->bg[state].green / 257;
272                         b = rc->bg[state].blue / 257;
273                 } else if (attr == "base") {
274                         r = rc->base[state].red / 257;
275                         g = rc->base[state].green / 257;
276                         b = rc->base[state].blue / 257;
277                 } else if (attr == "text") {
278                         r = rc->text[state].red / 257;
279                         g = rc->text[state].green / 257;
280                         b = rc->text[state].blue / 257;
281                 }
282         } else {
283                 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
284         }
285
286         window->remove ();
287
288         if (state == Gtk::STATE_NORMAL && rgba) {
289                 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
290         } else {
291                 return (uint32_t) RGB_TO_UINT(r,g,b);
292         }
293 }
294
295 bool
296 rgba_p_from_style (string style, float *r, float *g, float *b, string attr, int state)
297 {
298         static Gtk::Window* window = 0;
299         assert (r && g && b);
300
301         if (window == 0) {
302                 window = new Window (WINDOW_TOPLEVEL);
303         }
304
305         Gtk::EventBox foo;
306
307         window->add (foo);
308
309         foo.set_name (style);
310         foo.ensure_style ();
311
312         GtkRcStyle* rc = foo.get_style()->gobj()->rc_style;
313
314         if (!rc) {
315                 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
316                 return false;
317         }
318         if (attr == "fg") {
319                 *r = rc->fg[state].red / 65535.0;
320                 *g = rc->fg[state].green / 65535.0;
321                 *b = rc->fg[state].blue / 65535.0;
322         } else if (attr == "bg") {
323                 *r = rc->bg[state].red / 65535.0;
324                 *g = rc->bg[state].green / 65535.0;
325                 *b = rc->bg[state].blue / 65535.0;
326         } else if (attr == "base") {
327                 *r = rc->base[state].red / 65535.0;
328                 *g = rc->base[state].green / 65535.0;
329                 *b = rc->base[state].blue / 65535.0;
330         } else if (attr == "text") {
331                 *r = rc->text[state].red / 65535.0;
332                 *g = rc->text[state].green / 65535.0;
333                 *b = rc->text[state].blue / 65535.0;
334         } else {
335                 return false;
336         }
337
338         window->remove ();
339         return true;
340 }
341
342 void
343 set_color (Gdk::Color& c, int rgb)
344 {
345         c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
346 }
347
348 bool
349 relay_key_press (GdkEventKey* ev, Gtk::Window* win)
350 {
351         PublicEditor& ed (PublicEditor::instance());
352
353         if (!key_press_focus_accelerator_handler (*win, ev)) {
354                 if (&ed == 0) {
355                         /* early key press in pre-main-window-dialogs, no editor yet */
356                         return false;
357                 }
358                 return ed.on_key_press_event(ev);
359         } else {
360                 return true;
361         }
362 }
363
364 bool
365 forward_key_press (GdkEventKey* ev)
366 {
367         return PublicEditor::instance().on_key_press_event(ev);
368 }
369
370 bool
371 emulate_key_event (Gtk::Widget* w, unsigned int keyval)
372 {
373         GdkDisplay  *display = gtk_widget_get_display (GTK_WIDGET(w->gobj()));
374         GdkKeymap   *keymap  = gdk_keymap_get_for_display (display);
375         GdkKeymapKey *keymapkey = NULL;
376         gint n_keys;
377
378         if (!gdk_keymap_get_entries_for_keyval(keymap, keyval, &keymapkey, &n_keys)) return false;
379         if (n_keys !=1) { g_free(keymapkey); return false;}
380
381         GdkEventKey ev;
382         ev.type = GDK_KEY_PRESS;
383         ev.window = gtk_widget_get_window(GTK_WIDGET(w->gobj()));
384         ev.send_event = FALSE;
385         ev.time = 0;
386         ev.state = 0;
387         ev.keyval = keyval;
388         ev.length = 0;
389         ev.string = const_cast<gchar*> ("");
390         ev.hardware_keycode = keymapkey[0].keycode;
391         ev.group = keymapkey[0].group;
392         g_free(keymapkey);
393
394         forward_key_press(&ev);
395         ev.type = GDK_KEY_RELEASE;
396         return forward_key_press(&ev);
397 }
398
399 bool
400 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
401 {
402         GtkWindow* win = window.gobj();
403         GtkWidget* focus = gtk_window_get_focus (win);
404         bool special_handling_of_unmodified_accelerators = false;
405         bool allow_activating = true;
406         /* consider all relevant modifiers but not LOCK or SHIFT */
407         const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
408
409         if (focus) {
410                 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
411                         special_handling_of_unmodified_accelerators = true;
412                 }
413         }
414
415 #ifdef GTKOSX
416         /* at one time this appeared to be necessary. As of July 2012, it does not
417            appear to be. if it ever is necessar, figure out if it should apply
418            to all platforms.
419         */
420 #if 0 
421         if (Keyboard::some_magic_widget_has_focus ()) {
422                 allow_activating = false;
423         }
424 #endif
425 #endif
426
427
428         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",
429                                                           win,
430                                                           ev->keyval,
431                                                           ev->state,
432                                                           special_handling_of_unmodified_accelerators,
433                                                           Keyboard::some_magic_widget_has_focus(),
434                                                           allow_activating,
435                                                           focus));
436
437         /* This exists to allow us to override the way GTK handles
438            key events. The normal sequence is:
439
440            a) event is delivered to a GtkWindow
441            b) accelerators/mnemonics are activated
442            c) if (b) didn't handle the event, propagate to
443                the focus widget and/or focus chain
444
445            The problem with this is that if the accelerators include
446            keys without modifiers, such as the space bar or the
447            letter "e", then pressing the key while typing into
448            a text entry widget results in the accelerator being
449            activated, instead of the desired letter appearing
450            in the text entry.
451
452            There is no good way of fixing this, but this
453            represents a compromise. The idea is that
454            key events involving modifiers (not Shift)
455            get routed into the activation pathway first, then
456            get propagated to the focus widget if necessary.
457
458            If the key event doesn't involve modifiers,
459            we deliver to the focus widget first, thus allowing
460            it to get "normal text" without interference
461            from acceleration.
462
463            Of course, this can also be problematic: if there
464            is a widget with focus, then it will swallow
465            all "normal text" accelerators.
466         */
467
468         if (!special_handling_of_unmodified_accelerators) {
469
470                 /* XXX note that for a brief moment, the conditional above
471                  * included "|| (ev->state & mask)" so as to enforce the
472                  * implication of special_handling_of_UNMODIFIED_accelerators.
473                  * however, this forces any key that GTK doesn't allow and that
474                  * we have an alternative (see next comment) for to be
475                  * automatically sent through the accel groups activation
476                  * pathway, which prevents individual widgets & canvas items
477                  * from ever seeing it if is used by a key binding.
478                  * 
479                  * specifically, this hid Ctrl-down-arrow from MIDI region
480                  * views because it is also bound to an action.
481                  *
482                  * until we have a robust, clean binding system, this
483                  * quirk will have to remain in place.
484                  */
485
486                 /* pretend that certain key events that GTK does not allow
487                    to be used as accelerators are actually something that
488                    it does allow. but only where there are no modifiers.
489                 */
490
491                 uint32_t fakekey = ev->keyval;
492
493                 if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
494                         DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tactivate (was %1 now %2) without special hanlding of unmodified accels\n",
495                                                                           ev->keyval, fakekey));
496
497                         GdkModifierType mod = GdkModifierType (ev->state);
498
499                         mod = GdkModifierType (mod & gtk_accelerator_get_default_mod_mask());
500 #ifdef GTKOSX
501                         /* GTK on OS X is currently (February 2012) setting both
502                            the Meta and Mod2 bits in the event modifier state if 
503                            the Command key is down.
504
505                            gtk_accel_groups_activate() does not invoke any of the logic
506                            that gtk_window_activate_key() will that sorts out that stupid
507                            state of affairs, and as a result it fails to find a match
508                            for the key event and the current set of accelerators.
509
510                            to fix this, if the meta bit is set, remove the mod2 bit
511                            from the modifier. this assumes that our bindings use Primary
512                            which will have set the meta bit in the accelerator entry.
513                         */
514                         if (mod & GDK_META_MASK) {
515                                 mod = GdkModifierType (mod & ~GDK_MOD2_MASK);
516                         }
517 #endif
518
519                         if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, mod)) {
520                                 DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n");
521                                 return true;
522                         }
523                 }
524         }
525
526         if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
527
528                 /* no special handling or there are modifiers in effect: accelerate first */
529
530                 DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
531                 DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 string:%4 hardware_keycode:%5 group:%6\n",
532                                         ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group));
533
534                 if (allow_activating) {
535                         DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
536                         if (gtk_window_activate_key (win, ev)) {
537                                 DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
538                                 return true;
539                         }
540                 } else {
541                         DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
542                 }
543
544                 DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
545
546                 return gtk_window_propagate_key_event (win, ev);
547         }
548
549         /* no modifiers, propagate first */
550
551         DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
552
553         if (!gtk_window_propagate_key_event (win, ev)) {
554                 DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
555                 if (allow_activating) {
556                         return gtk_window_activate_key (win, ev);
557                 } else {
558                         DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
559                 }
560
561         } else {
562                 DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
563                 return true;
564         }
565
566         DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
567         return true;
568 }
569
570 Glib::RefPtr<Gdk::Pixbuf>
571 get_xpm (std::string name)
572 {
573         if (!xpm_map[name]) {
574
575                 Searchpath spath(ARDOUR::ardour_data_search_path());
576
577                 spath.add_subdirectory_to_paths("pixmaps");
578
579                 std::string data_file_path;
580
581                 if(!find_file_in_search_path (spath, name, data_file_path)) {
582                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
583                 }
584
585                 try {
586                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path);
587                 } catch(const Glib::Error& e)   {
588                         warning << "Caught Glib::Error: " << e.what() << endmsg;
589                 }
590         }
591
592         return xpm_map[name];
593 }
594
595 std::string
596 get_icon_path (const char* cname)
597 {
598         string name = cname;
599         name += X_(".png");
600
601         Searchpath spath(ARDOUR::ardour_data_search_path());
602
603         spath.add_subdirectory_to_paths("icons");
604
605         std::string data_file_path;
606
607         if (!find_file_in_search_path (spath, name, data_file_path)) {
608                 fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
609         }
610
611         return data_file_path;
612 }
613
614 Glib::RefPtr<Gdk::Pixbuf>
615 get_icon (const char* cname)
616 {
617         Glib::RefPtr<Gdk::Pixbuf> img;
618         try {
619                 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
620         } catch (const Gdk::PixbufError &e) {
621                 cerr << "Caught PixbufError: " << e.what() << endl;
622         } catch (...) {
623                 error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg;
624         }
625
626         return img;
627 }
628
629 string
630 longest (vector<string>& strings)
631 {
632         if (strings.empty()) {
633                 return string ("");
634         }
635
636         vector<string>::iterator longest = strings.begin();
637         string::size_type longest_length = (*longest).length();
638
639         vector<string>::iterator i = longest;
640         ++i;
641
642         while (i != strings.end()) {
643
644                 string::size_type len = (*i).length();
645
646                 if (len > longest_length) {
647                         longest = i;
648                         longest_length = len;
649                 }
650
651                 ++i;
652         }
653
654         return *longest;
655 }
656
657 bool
658 key_is_legal_for_numeric_entry (guint keyval)
659 {
660         /* we assume that this does not change over the life of the process 
661          */
662
663         static int comma_decimal = -1;
664
665         switch (keyval) {
666         case GDK_period:
667         case GDK_comma:
668                 if (comma_decimal < 0) {
669                         std::lconv* lc = std::localeconv();
670                         if (strchr (lc->decimal_point, ',') != 0) {
671                                 comma_decimal = 1;
672                         } else {
673                                 comma_decimal = 0;
674                         }
675                 }
676                 break;
677         default:
678                 break;
679         }
680
681         switch (keyval) {
682         case GDK_decimalpoint:
683         case GDK_KP_Separator:
684                 return true;
685
686         case GDK_period:
687                 if (comma_decimal) {
688                         return false;
689                 } else {
690                         return true;
691                 }
692                 break;
693         case GDK_comma:
694                 if (comma_decimal) {
695                         return true;
696                 } else {
697                         return false;
698                 }
699                 break;
700         case GDK_minus:
701         case GDK_plus:
702         case GDK_0:
703         case GDK_1:
704         case GDK_2:
705         case GDK_3:
706         case GDK_4:
707         case GDK_5:
708         case GDK_6:
709         case GDK_7:
710         case GDK_8:
711         case GDK_9:
712         case GDK_KP_Add:
713         case GDK_KP_Subtract:
714         case GDK_KP_Decimal:
715         case GDK_KP_0:
716         case GDK_KP_1:
717         case GDK_KP_2:
718         case GDK_KP_3:
719         case GDK_KP_4:
720         case GDK_KP_5:
721         case GDK_KP_6:
722         case GDK_KP_7:
723         case GDK_KP_8:
724         case GDK_KP_9:
725         case GDK_Return:
726         case GDK_BackSpace:
727         case GDK_Delete:
728         case GDK_KP_Enter:
729         case GDK_Home:
730         case GDK_End:
731         case GDK_Left:
732         case GDK_Right:
733                 return true;
734
735         default:
736                 break;
737         }
738
739         return false;
740 }
741 void
742 set_pango_fontsize ()
743 {
744         long val = ARDOUR::Config->get_font_scale();
745
746         /* FT2 rendering - used by GnomeCanvas, sigh */
747
748 #ifndef PLATFORM_WINDOWS
749         pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_new(), val/1024, val/1024);
750 #endif
751
752         /* Cairo rendering, in case there is any */
753
754         pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
755 }
756
757 void
758 reset_dpi ()
759 {
760         long val = ARDOUR::Config->get_font_scale();
761         set_pango_fontsize ();
762         /* Xft rendering */
763
764         gtk_settings_set_long_property (gtk_settings_get_default(),
765                                         "gtk-xft-dpi", val, "ardour");
766         DPIReset();//Emit Signal
767 }
768
769 void
770 resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
771 {
772         Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
773         Gdk::Rectangle monitor_rect;
774         screen->get_monitor_geometry (0, monitor_rect);
775
776         int const w = std::min (int (monitor_rect.get_width() * 0.8), max_width);
777         int const h = std::min (int (monitor_rect.get_height() * 0.8), max_height);
778
779         window->resize (w, h);
780 }
781
782
783 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
784 string
785 escape_underscores (string const & s)
786 {
787         string o;
788         string::size_type const N = s.length ();
789
790         for (string::size_type i = 0; i < N; ++i) {
791                 if (s[i] == '_') {
792                         o += "__";
793                 } else {
794                         o += s[i];
795                 }
796         }
797
798         return o;
799 }
800
801 /** Replace < and > with &lt; and &gt; respectively to make < > display correctly in markup strings */
802 string
803 escape_angled_brackets (string const & s)
804 {
805         string o = s;
806         boost::replace_all (o, "<", "&lt;");
807         boost::replace_all (o, ">", "&gt;");
808         return o;
809 }
810
811 Gdk::Color
812 unique_random_color (list<Gdk::Color>& used_colors)
813 {
814         Gdk::Color newcolor;
815
816         while (1) {
817
818                 double h, s, v;
819
820                 h = fmod (random(), 360.0);
821                 s = (random() % 65535) / 65535.0;
822                 v = (random() % 65535) / 65535.0;
823
824                 s = min (0.5, s); /* not too saturated */
825                 v = max (0.9, v);  /* not too bright */
826                 newcolor.set_hsv (h, s, v);
827
828                 if (used_colors.size() == 0) {
829                         used_colors.push_back (newcolor);
830                         return newcolor;
831                 }
832
833                 for (list<Gdk::Color>::iterator i = used_colors.begin(); i != used_colors.end(); ++i) {
834                   Gdk::Color c = *i;
835                         float rdelta, bdelta, gdelta;
836
837                         rdelta = newcolor.get_red() - c.get_red();
838                         bdelta = newcolor.get_blue() - c.get_blue();
839                         gdelta = newcolor.get_green() - c.get_green();
840
841                         if (sqrt (rdelta*rdelta + bdelta*bdelta + gdelta*gdelta) > 25.0) {
842                                 /* different enough */
843                                 used_colors.push_back (newcolor);
844                                 return newcolor;
845                         }
846                 }
847
848                 /* XXX need throttle here to make sure we don't spin for ever */
849         }
850 }
851
852 string 
853 rate_as_string (float r)
854 {
855         char buf[32];
856         if (fmod (r, 1000.0f)) {
857                 snprintf (buf, sizeof (buf), "%.1f kHz", r/1000.0);
858         } else {
859                 snprintf (buf, sizeof (buf), "%.0f kHz", r/1000.0);
860         }
861         return buf;
862 }