changes to help strp silence
[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 #include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
21 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
22
23 #include <cstdlib>
24 #include <cctype>
25 #include <fstream>
26 #include <sys/stat.h>
27 #include <libart_lgpl/art_misc.h>
28 #include <gtkmm/rc.h>
29 #include <gtkmm/window.h>
30 #include <gtkmm/combo.h>
31 #include <gtkmm/label.h>
32 #include <gtkmm/paned.h>
33 #include <gtk/gtkpaned.h>
34
35 #include "pbd/file_utils.h"
36
37 #include <gtkmm2ext/utils.h>
38 #include "ardour/configuration.h"
39 #include "ardour/rc_configuration.h"
40
41 #include "ardour/filesystem_paths.h"
42
43 #include "ardour_ui.h"
44 #include "public_editor.h"
45 #include "keyboard.h"
46 #include "utils.h"
47 #include "i18n.h"
48 #include "rgb_macros.h"
49 #include "canvas_impl.h"
50
51 using namespace std;
52 using namespace Gtk;
53 using namespace Glib;
54 using namespace PBD;
55 using Gtkmm2ext::Keyboard;
56
57 sigc::signal<void>  DPIReset;
58
59 int
60 pixel_width (const ustring& str, Pango::FontDescription& font)
61 {
62         Label foo;
63         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
64
65         layout->set_font_description (font);
66         layout->set_text (str);
67
68         int width, height;
69         Gtkmm2ext::get_ink_pixel_size (layout, width, height);
70         return width;
71 }
72
73 ustring
74 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
75 {
76         Label foo;
77         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
78         ustring::size_type shorter_by = 0;
79         ustring txt;
80
81         layout->set_font_description (font);
82
83         actual_width = 0;
84
85         ustring ustr = str;
86         ustring::iterator last = ustr.end();
87         --last; /* now points at final entry */
88
89         txt = ustr;
90
91         while (!ustr.empty()) {
92
93                 layout->set_text (txt);
94
95                 int width, height;
96                 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
97
98                 if (width < pixel_width) {
99                         actual_width = width;
100                         break;
101                 }
102
103                 ustr.erase (last--);
104                 shorter_by++;
105
106                 if (with_ellipses && shorter_by > 3) {
107                         txt = ustr;
108                         txt += "...";
109                 } else {
110                         txt = ustr;
111                 }
112         }
113
114         return txt;
115 }
116
117 /** Try to fit a string into a given horizontal space by ellipsizing it.
118  *  @param cr Cairo context in which the text will be plotted.
119  *  @param name Text.
120  *  @param avail Available horizontal space.
121  *  @return (Text, possibly ellipsized) and (horizontal size of text)
122  */
123
124 std::pair<std::string, double>
125 fit_to_pixels (cairo_t* cr, std::string name, double avail)
126 {
127         /* XXX hopefully there exists a more efficient way of doing this */
128
129         bool abbreviated = false;
130         uint32_t width = 0;
131
132         while (1) {
133                 cairo_text_extents_t ext;
134                 cairo_text_extents (cr, name.c_str(), &ext);
135
136                 if (ext.width < avail || name.length() <= 4) {
137                         width = ext.width;
138                         break;
139                 }
140
141                 if (abbreviated) {
142                         name = name.substr (0, name.length() - 4) + "...";
143                 } else {
144                         name = name.substr (0, name.length() - 3) + "...";
145                         abbreviated = true;
146                 }
147         }
148
149         return std::make_pair (name, width);
150 }
151
152
153 /** Add an element to a menu, settings its sensitivity.
154  * @param m Menu to add to.
155  * @param e Element to add.
156  * @param s true to make sensitive, false to make insensitive
157  */
158 void
159 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
160 {
161         m.push_back (e);
162         if (!s) {
163                 m.back().set_sensitive (false);
164         }
165 }
166
167
168 gint
169 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
170 {
171         win->hide ();
172         return 0;
173 }
174
175 /* xpm2rgb copied from nixieclock, which bore the legend:
176
177     nixieclock - a nixie desktop timepiece
178     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
179
180     and was released under the GPL.
181 */
182
183 unsigned char*
184 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
185 {
186         static long vals[256], val;
187         uint32_t t, x, y, colors, cpp;
188         unsigned char c;
189         unsigned char *savergb, *rgb;
190
191         // PARSE HEADER
192
193         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
194                 error << string_compose (_("bad XPM header %1"), xpm[0])
195                       << endmsg;
196                 return 0;
197         }
198
199         savergb = rgb = (unsigned char*) malloc (h * w * 3);
200
201         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
202         for (t = 0; t < colors; ++t) {
203                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
204                 vals[c] = val;
205         }
206
207         // COLORMAP -> RGB CONVERSION
208         //    Get low 3 bytes from vals[]
209         //
210
211         const char *p;
212         for (y = h-1; y > 0; --y) {
213
214                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
215                         val = vals[(int)*p++];
216                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
217                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
218                         *(rgb+0) = val & 0xff;             // 0:R
219                 }
220         }
221
222         return (savergb);
223 }
224
225 unsigned char*
226 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
227 {
228         static long vals[256], val;
229         uint32_t t, x, y, colors, cpp;
230         unsigned char c;
231         unsigned char *savergb, *rgb;
232         char transparent;
233
234         // PARSE HEADER
235
236         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
237                 error << string_compose (_("bad XPM header %1"), xpm[0])
238                       << endmsg;
239                 return 0;
240         }
241
242         savergb = rgb = (unsigned char*) malloc (h * w * 4);
243
244         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
245
246         if (strstr (xpm[1], "None")) {
247                 sscanf (xpm[1], "%c", &transparent);
248                 t = 1;
249         } else {
250                 transparent = 0;
251                 t = 0;
252         }
253
254         for (; t < colors; ++t) {
255                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
256                 vals[c] = val;
257         }
258
259         // COLORMAP -> RGB CONVERSION
260         //    Get low 3 bytes from vals[]
261         //
262
263         const char *p;
264         for (y = h-1; y > 0; --y) {
265
266                 char alpha;
267
268                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
269
270                         if (transparent && (*p++ == transparent)) {
271                                 alpha = 0;
272                                 val = 0;
273                         } else {
274                                 alpha = 255;
275                                 val = vals[(int)*p];
276                         }
277
278                         *(rgb+3) = alpha;                  // 3: alpha
279                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
280                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
281                         *(rgb+0) = val & 0xff;             // 0:R
282                 }
283         }
284
285         return (savergb);
286 }
287
288 ArdourCanvas::Points*
289 get_canvas_points (string /*who*/, uint32_t npoints)
290 {
291         // cerr << who << ": wants " << npoints << " canvas points" << endl;
292 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
293         if (npoints > (uint32_t) gdk_screen_width() + 4) {
294                 abort ();
295         }
296 #endif
297         return new ArdourCanvas::Points (npoints);
298 }
299
300 Pango::FontDescription*
301 get_font_for_style (string widgetname)
302 {
303         Gtk::Window window (WINDOW_TOPLEVEL);
304         Gtk::Label foobar;
305         Glib::RefPtr<Gtk::Style> style;
306
307         window.add (foobar);
308         foobar.set_name (widgetname);
309         foobar.ensure_style();
310
311         style = foobar.get_style ();
312
313         Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
314
315         PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
316
317         if (!pfd) {
318
319                 /* layout inherited its font description from a PangoContext */
320
321                 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
322                 pfd =  pango_context_get_font_description (ctxt);
323                 return new Pango::FontDescription (pfd, true); /* make a copy */
324         }
325
326         return new Pango::FontDescription (pfd, true); /* make a copy */
327 }
328
329 uint32_t
330 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
331 {
332         /* In GTK+2, styles aren't set up correctly if the widget is not
333            attached to a toplevel window that has a screen pointer.
334         */
335
336         static Gtk::Window* window = 0;
337
338         if (window == 0) {
339                 window = new Window (WINDOW_TOPLEVEL);
340         }
341
342         Gtk::Label foo;
343
344         window->add (foo);
345
346         foo.set_name (style);
347         foo.ensure_style ();
348
349         GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
350
351         if (waverc) {
352                 if (attr == "fg") {
353                         r = waverc->fg[state].red / 257;
354                         g = waverc->fg[state].green / 257;
355                         b = waverc->fg[state].blue / 257;
356
357                         /* what a hack ... "a" is for "active" */
358                         if (state == Gtk::STATE_NORMAL && rgba) {
359                                 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
360                         }
361                 } else if (attr == "bg") {
362                         r = g = b = 0;
363                         r = waverc->bg[state].red / 257;
364                         g = waverc->bg[state].green / 257;
365                         b = waverc->bg[state].blue / 257;
366                 } else if (attr == "base") {
367                         r = waverc->base[state].red / 257;
368                         g = waverc->base[state].green / 257;
369                         b = waverc->base[state].blue / 257;
370                 } else if (attr == "text") {
371                         r = waverc->text[state].red / 257;
372                         g = waverc->text[state].green / 257;
373                         b = waverc->text[state].blue / 257;
374                 }
375         } else {
376                 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
377         }
378
379         window->remove ();
380
381         if (state == Gtk::STATE_NORMAL && rgba) {
382                 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
383         } else {
384                 return (uint32_t) RGB_TO_UINT(r,g,b);
385         }
386 }
387
388
389 Gdk::Color
390 color_from_style (string widget_style_name, int state, string attr)
391 {
392         GtkStyle* style;
393
394         style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
395                                            widget_style_name.c_str(),
396                                            0, G_TYPE_NONE);
397
398         if (!style) {
399                 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
400                 return Gdk::Color ("red");
401         }
402
403         if (attr == "fg") {
404                 return Gdk::Color (&style->fg[state]);
405         }
406
407         if (attr == "bg") {
408                 return Gdk::Color (&style->bg[state]);
409         }
410
411         if (attr == "light") {
412                 return Gdk::Color (&style->light[state]);
413         }
414
415         if (attr == "dark") {
416                 return Gdk::Color (&style->dark[state]);
417         }
418
419         if (attr == "mid") {
420                 return Gdk::Color (&style->mid[state]);
421         }
422
423         if (attr == "text") {
424                 return Gdk::Color (&style->text[state]);
425         }
426
427         if (attr == "base") {
428                 return Gdk::Color (&style->base[state]);
429         }
430
431         if (attr == "text_aa") {
432                 return Gdk::Color (&style->text_aa[state]);
433         }
434
435         error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
436         return Gdk::Color ("red");
437 }
438
439 Glib::RefPtr<Gdk::GC>
440 gc_from_style (string widget_style_name, int state, string attr)
441 {
442         GtkStyle* style;
443
444         style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
445                                            widget_style_name.c_str(),
446                                            0, G_TYPE_NONE);
447
448         if (!style) {
449                 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
450                 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
451                 ret->set_rgb_fg_color(Gdk::Color("red"));
452                 return ret;
453         }
454
455         if (attr == "fg") {
456                 return Glib::wrap(style->fg_gc[state]);
457         }
458
459         if (attr == "bg") {
460                 return Glib::wrap(style->bg_gc[state]);
461         }
462
463         if (attr == "light") {
464                 return Glib::wrap(style->light_gc[state]);
465         }
466
467         if (attr == "dark") {
468                 return Glib::wrap(style->dark_gc[state]);
469         }
470
471         if (attr == "mid") {
472                 return Glib::wrap(style->mid_gc[state]);
473         }
474
475         if (attr == "text") {
476                 return Glib::wrap(style->text_gc[state]);
477         }
478
479         if (attr == "base") {
480                 return Glib::wrap(style->base_gc[state]);
481         }
482
483         if (attr == "text_aa") {
484                 return Glib::wrap(style->text_aa_gc[state]);
485         }
486
487         error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
488         Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
489         ret->set_rgb_fg_color(Gdk::Color("red"));
490         return ret;
491 }
492
493
494 bool
495 canvas_item_visible (ArdourCanvas::Item* item)
496 {
497         return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
498 }
499
500 void
501 set_color (Gdk::Color& c, int rgb)
502 {
503         c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
504 }
505
506 #ifdef GTKOSX
507 extern "C" {
508         gboolean gdk_quartz_possibly_forward (GdkEvent*);
509 }
510 #endif
511
512 bool
513 relay_key_press (GdkEventKey* ev, Gtk::Window* win)
514 {
515         if (!key_press_focus_accelerator_handler (*win, ev)) {
516                 return PublicEditor::instance().on_key_press_event(ev);
517         } else {
518                 return true;
519         }
520 }
521
522 bool
523 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
524 {
525         GtkWindow* win = window.gobj();
526         GtkWidget* focus = gtk_window_get_focus (win);
527         bool special_handling_of_unmodified_accelerators = false;
528         bool allow_activating = true;
529
530 #undef DEBUG_ACCELERATOR_HANDLING
531 #ifdef  DEBUG_ACCELERATOR_HANDLING
532         //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
533         bool debug=true;
534 #endif
535         if (focus) {
536                 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
537                         special_handling_of_unmodified_accelerators = true;
538                 }
539         }
540
541 #ifdef GTKOSX
542         /* should this be universally true? */
543         if (Keyboard::some_magic_widget_has_focus ()) {
544                 allow_activating = false;
545         }
546 #endif
547
548 #ifdef DEBUG_ACCELERATOR_HANDLING
549         if (debug) {
550                 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
551                      << special_handling_of_unmodified_accelerators
552                      << " magic widget focus ? "
553                      << Keyboard::some_magic_widget_has_focus()
554                      << " allow_activation ? "
555                      << allow_activating
556                      << endl;
557         }
558 #endif
559
560         /* This exists to allow us to override the way GTK handles
561            key events. The normal sequence is:
562
563            a) event is delivered to a GtkWindow
564            b) accelerators/mnemonics are activated
565            c) if (b) didn't handle the event, propagate to
566                the focus widget and/or focus chain
567
568            The problem with this is that if the accelerators include
569            keys without modifiers, such as the space bar or the
570            letter "e", then pressing the key while typing into
571            a text entry widget results in the accelerator being
572            activated, instead of the desired letter appearing
573            in the text entry.
574
575            There is no good way of fixing this, but this
576            represents a compromise. The idea is that
577            key events involving modifiers (not Shift)
578            get routed into the activation pathway first, then
579            get propagated to the focus widget if necessary.
580
581            If the key event doesn't involve modifiers,
582            we deliver to the focus widget first, thus allowing
583            it to get "normal text" without interference
584            from acceleration.
585
586            Of course, this can also be problematic: if there
587            is a widget with focus, then it will swallow
588            all "normal text" accelerators.
589         */
590
591
592         if (!special_handling_of_unmodified_accelerators) {
593
594                 /* pretend that certain key events that GTK does not allow
595                    to be used as accelerators are actually something that
596                    it does allow.
597                 */
598
599                 uint32_t fakekey = ev->keyval;
600
601                 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
602                         if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
603                                 return true;
604                         }
605
606 #ifdef GTKOSX
607                         if (allow_activating) {
608                                 int oldval = ev->keyval;
609                                 ev->keyval = fakekey;
610                                 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
611                                         return true;
612                                 }
613                                 ev->keyval = oldval;
614                         }
615 #endif
616                 }
617         }
618
619         /* consider all relevant modifiers but not LOCK or SHIFT */
620
621         guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
622
623         if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
624
625                 /* no special handling or there are modifiers in effect: accelerate first */
626
627 #ifdef DEBUG_ACCELERATOR_HANDLING
628                 if (debug) {
629                         cerr << "\tactivate, then propagate\n";
630                 }
631 #endif
632
633                 if (allow_activating) {
634 #ifdef GTKOSX
635                         if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
636                                 return true;
637                         }
638 #endif
639                         if (gtk_window_activate_key (win, ev)) {
640                                 return true;
641                         }
642                 }
643
644 #ifdef DEBUG_ACCELERATOR_HANDLING
645                 if (debug) {
646                         cerr << "\tnot accelerated, now propagate\n";
647                 }
648 #endif
649                 return gtk_window_propagate_key_event (win, ev);
650         }
651
652         /* no modifiers, propagate first */
653
654 #ifdef DEBUG_ACCELERATOR_HANDLING
655         if (debug) {
656                 cerr << "\tpropagate, then activate\n";
657         }
658 #endif
659         if (!gtk_window_propagate_key_event (win, ev)) {
660 #ifdef DEBUG_ACCELERATOR_HANDLING
661                 if (debug) {
662                         cerr << "\tpropagation didn't handle, so activate\n";
663                 }
664 #endif
665
666                 if (allow_activating) {
667
668 #ifdef GTKOSX
669                         if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
670                                 return true;
671                         }
672 #endif
673                         return gtk_window_activate_key (win, ev);
674                 }
675
676         } else {
677 #ifdef DEBUG_ACCELERATOR_HANDLING
678                 if (debug) {
679                         cerr << "\thandled by propagate\n";
680                 }
681 #endif
682                 return true;
683         }
684
685 #ifdef DEBUG_ACCELERATOR_HANDLING
686         if (debug) {
687                 cerr << "\tnot handled\n";
688         }
689 #endif
690         return true;
691 }
692
693 Glib::RefPtr<Gdk::Pixbuf>
694 get_xpm (std::string name)
695 {
696         if (!xpm_map[name]) {
697
698                 SearchPath spath(ARDOUR::ardour_search_path());
699                 spath += ARDOUR::system_data_search_path();
700
701                 spath.add_subdirectory_to_paths("pixmaps");
702
703                 sys::path data_file_path;
704
705                 if(!find_file_in_search_path (spath, name, data_file_path)) {
706                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
707                 }
708
709                 try {
710                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path.to_string());
711                 } catch(const Glib::Error& e)   {
712                         warning << "Caught Glib::Error: " << e.what() << endmsg;
713                 }
714         }
715
716         return xpm_map[name];
717 }
718
719 Glib::ustring
720 get_icon_path (const char* cname)
721 {
722         string name = cname;
723         name += X_(".png");
724
725         SearchPath spath(ARDOUR::ardour_search_path());
726         spath += ARDOUR::system_data_search_path();
727
728         spath.add_subdirectory_to_paths("icons");
729
730         sys::path data_file_path;
731
732         if (!find_file_in_search_path (spath, name, data_file_path)) {
733                 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
734         }
735
736         return data_file_path.to_string();
737 }
738
739 Glib::RefPtr<Gdk::Pixbuf>
740 get_icon (const char* cname)
741 {
742         Glib::RefPtr<Gdk::Pixbuf> img;
743         try {
744                 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
745         } catch (const Gdk::PixbufError &e) {
746                 cerr << "Caught PixbufError: " << e.what() << endl;
747         } catch (...) {
748                 g_message("Caught ... ");
749         }
750
751         return img;
752 }
753
754 string
755 longest (vector<string>& strings)
756 {
757         if (strings.empty()) {
758                 return string ("");
759         }
760
761         vector<string>::iterator longest = strings.begin();
762         string::size_type longest_length = (*longest).length();
763
764         vector<string>::iterator i = longest;
765         ++i;
766
767         while (i != strings.end()) {
768
769                 string::size_type len = (*i).length();
770
771                 if (len > longest_length) {
772                         longest = i;
773                         longest_length = len;
774                 }
775
776                 ++i;
777         }
778
779         return *longest;
780 }
781
782 bool
783 key_is_legal_for_numeric_entry (guint keyval)
784 {
785         switch (keyval) {
786         case GDK_minus:
787         case GDK_plus:
788         case GDK_period:
789         case GDK_comma:
790         case GDK_0:
791         case GDK_1:
792         case GDK_2:
793         case GDK_3:
794         case GDK_4:
795         case GDK_5:
796         case GDK_6:
797         case GDK_7:
798         case GDK_8:
799         case GDK_9:
800         case GDK_KP_Add:
801         case GDK_KP_Subtract:
802         case GDK_KP_Decimal:
803         case GDK_KP_0:
804         case GDK_KP_1:
805         case GDK_KP_2:
806         case GDK_KP_3:
807         case GDK_KP_4:
808         case GDK_KP_5:
809         case GDK_KP_6:
810         case GDK_KP_7:
811         case GDK_KP_8:
812         case GDK_KP_9:
813         case GDK_Return:
814         case GDK_BackSpace:
815         case GDK_Delete:
816         case GDK_KP_Enter:
817         case GDK_Home:
818         case GDK_End:
819         case GDK_Left:
820         case GDK_Right:
821                 return true;
822
823         default:
824                 break;
825         }
826
827         return false;
828 }
829 void
830 set_pango_fontsize ()
831 {
832         long val = ARDOUR::Config->get_font_scale();
833
834         /* FT2 rendering - used by GnomeCanvas, sigh */
835
836         pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
837
838         /* Cairo rendering, in case there is any */
839
840         pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
841 }
842
843 void
844 reset_dpi ()
845 {
846         long val = ARDOUR::Config->get_font_scale();
847         set_pango_fontsize ();
848         /* Xft rendering */
849
850         gtk_settings_set_long_property (gtk_settings_get_default(),
851                                         "gtk-xft-dpi", val, "ardour");
852         DPIReset();//Emit Signal
853 }
854
855 bool
856 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
857 {
858         int fakekey = GDK_VoidSymbol;
859
860         switch (keyval) {
861         case GDK_Tab:
862         case GDK_ISO_Left_Tab:
863                 fakekey = GDK_nabla;
864                 break;
865
866         case GDK_Up:
867                 fakekey = GDK_uparrow;
868                 break;
869
870         case GDK_Down:
871                 fakekey = GDK_downarrow;
872                 break;
873
874         case GDK_Right:
875                 fakekey = GDK_rightarrow;
876                 break;
877
878         case GDK_Left:
879                 fakekey = GDK_leftarrow;
880                 break;
881
882         case GDK_Return:
883                 fakekey = GDK_3270_Enter;
884                 break;
885
886         case GDK_KP_Enter:
887                 fakekey = GDK_F35;
888                 break;
889
890         default:
891                 break;
892         }
893
894         if (fakekey != GDK_VoidSymbol) {
895                 keyval = fakekey;
896                 return true;
897         }
898
899         return false;
900 }
901
902 uint32_t
903 possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
904 {
905         switch (keyval) {
906         case GDK_nabla:
907                 return GDK_Tab;
908                 break;
909
910         case GDK_uparrow:
911                 return GDK_Up;
912                 break;
913
914         case GDK_downarrow:
915                 return GDK_Down;
916                 break;
917
918         case GDK_rightarrow:
919                 return GDK_Right;
920                 break;
921
922         case GDK_leftarrow:
923                 return GDK_Left;
924                 break;
925
926         case GDK_3270_Enter:
927                 return GDK_Return;
928
929         case GDK_F35:
930                 return GDK_KP_Enter;
931                 break;
932         }
933
934         return keyval;
935 }
936
937
938
939 inline guint8
940 convert_color_channel (guint8 src,
941                        guint8 alpha)
942 {
943         return alpha ? ((guint (src) << 8) - src) / alpha : 0;
944 }
945
946 void
947 convert_bgra_to_rgba (guint8 const* src,
948                       guint8*       dst,
949                       int           width,
950                       int           height)
951 {
952         guint8 const* src_pixel = src;
953         guint8*       dst_pixel = dst;
954
955         for (int y = 0; y < height; y++)
956                 for (int x = 0; x < width; x++)
957                 {
958                         dst_pixel[0] = convert_color_channel (src_pixel[2],
959                                                               src_pixel[3]);
960                         dst_pixel[1] = convert_color_channel (src_pixel[1],
961                                                               src_pixel[3]);
962                         dst_pixel[2] = convert_color_channel (src_pixel[0],
963                                                               src_pixel[3]);
964                         dst_pixel[3] = src_pixel[3];
965
966                         dst_pixel += 4;
967                         src_pixel += 4;
968                 }
969 }
970
971 void
972 resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
973 {
974         Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
975         Gdk::Rectangle monitor_rect;
976         screen->get_monitor_geometry (0, monitor_rect);
977
978         int const w = std::min (monitor_rect.get_width(), max_width) * 0.8;
979         int const h = std::min (monitor_rect.get_height(), max_height) * 0.8;
980
981         window->resize (w, h);
982 }
983
984 Glib::RefPtr<Gdk::Pixbuf>
985 pixbuf_from_ustring(const ustring& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg)
986 {
987         static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
988
989         if (name.empty()) {
990                 if (empty_pixbuf == 0) {
991                         empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
992                         *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
993                 }
994                 return *empty_pixbuf;
995         }
996
997         Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
998
999         cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
1000         cairo_t* cr = cairo_create (surface);
1001         cairo_text_extents_t te;
1002         
1003         cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
1004         cairo_select_font_face (cr, font->get_family().c_str(),
1005                                 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
1006         cairo_set_font_size (cr,  font->get_size() / Pango::SCALE);
1007         cairo_text_extents (cr, name.c_str(), &te);
1008         
1009         cairo_move_to (cr, 0.5, 0.5 - te.height / 2 - te.y_bearing + clip_height / 2);
1010         cairo_show_text (cr, name.c_str());
1011         
1012         convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
1013
1014         cairo_destroy(cr);
1015         cairo_surface_destroy(surface);
1016
1017         return buf;
1018 }
1019
1020 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
1021 string
1022 escape_underscores (string const & s)
1023 {
1024         string o;
1025         string::size_type const N = s.length ();
1026
1027         for (string::size_type i = 0; i < N; ++i) {
1028                 if (s[i] == '_') {
1029                         o += "__";
1030                 } else {
1031                         o += s[i];
1032                 }
1033         }
1034
1035         return o;
1036 }