various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / gtk2_ardour / utils.cc
blob8177e7facc1d274774c135d17a7f1a4d56f8c471
1 /*
2 Copyright (C) 2003 Paul Davis
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.
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.
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.
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
24 #include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
25 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
27 #include <cstdlib>
28 #include <cctype>
29 #include <fstream>
30 #include <sys/stat.h>
31 #include <libart_lgpl/art_misc.h>
32 #include <gtkmm/rc.h>
33 #include <gtkmm/window.h>
34 #include <gtkmm/combo.h>
35 #include <gtkmm/label.h>
36 #include <gtkmm/paned.h>
37 #include <gtk/gtkpaned.h>
39 #include "pbd/file_utils.h"
41 #include <gtkmm2ext/utils.h>
42 #include "ardour/configuration.h"
43 #include "ardour/rc_configuration.h"
45 #include "ardour/filesystem_paths.h"
47 #include "ardour_ui.h"
48 #include "debug.h"
49 #include "public_editor.h"
50 #include "keyboard.h"
51 #include "utils.h"
52 #include "i18n.h"
53 #include "rgb_macros.h"
54 #include "canvas_impl.h"
55 #include "gui_thread.h"
57 using namespace std;
58 using namespace Gtk;
59 using namespace Glib;
60 using namespace PBD;
61 using Gtkmm2ext::Keyboard;
63 sigc::signal<void> DPIReset;
65 int
66 pixel_width (const string& str, Pango::FontDescription& font)
68 Label foo;
69 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
71 layout->set_font_description (font);
72 layout->set_text (str);
74 int width, height;
75 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
76 return width;
79 string
80 fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
82 Label foo;
83 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
84 string::size_type shorter_by = 0;
85 string txt;
87 layout->set_font_description (font);
89 actual_width = 0;
91 string ustr = str;
92 string::iterator last = ustr.end();
93 --last; /* now points at final entry */
95 txt = ustr;
97 while (!ustr.empty()) {
99 layout->set_text (txt);
101 int width, height;
102 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
104 if (width < pixel_width) {
105 actual_width = width;
106 break;
109 ustr.erase (last--);
110 shorter_by++;
112 if (with_ellipses && shorter_by > 3) {
113 txt = ustr;
114 txt += "...";
115 } else {
116 txt = ustr;
120 return txt;
123 /** Try to fit a string into a given horizontal space by ellipsizing it.
124 * @param cr Cairo context in which the text will be plotted.
125 * @param name Text.
126 * @param avail Available horizontal space.
127 * @return (Text, possibly ellipsized) and (horizontal size of text)
130 std::pair<std::string, double>
131 fit_to_pixels (cairo_t* cr, std::string name, double avail)
133 /* XXX hopefully there exists a more efficient way of doing this */
135 bool abbreviated = false;
136 uint32_t width = 0;
138 while (1) {
139 cairo_text_extents_t ext;
140 cairo_text_extents (cr, name.c_str(), &ext);
142 if (ext.width < avail || name.length() <= 4) {
143 width = ext.width;
144 break;
147 if (abbreviated) {
148 name = name.substr (0, name.length() - 4) + "...";
149 } else {
150 name = name.substr (0, name.length() - 3) + "...";
151 abbreviated = true;
155 return std::make_pair (name, width);
159 /** Add an element to a menu, settings its sensitivity.
160 * @param m Menu to add to.
161 * @param e Element to add.
162 * @param s true to make sensitive, false to make insensitive
164 void
165 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
167 m.push_back (e);
168 if (!s) {
169 m.back().set_sensitive (false);
174 gint
175 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
177 win->hide ();
178 return 0;
181 /* xpm2rgb copied from nixieclock, which bore the legend:
183 nixieclock - a nixie desktop timepiece
184 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
186 and was released under the GPL.
189 unsigned char*
190 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
192 static long vals[256], val;
193 uint32_t t, x, y, colors, cpp;
194 unsigned char c;
195 unsigned char *savergb, *rgb;
197 // PARSE HEADER
199 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
200 error << string_compose (_("bad XPM header %1"), xpm[0])
201 << endmsg;
202 return 0;
205 savergb = rgb = (unsigned char*) malloc (h * w * 3);
207 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
208 for (t = 0; t < colors; ++t) {
209 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
210 vals[c] = val;
213 // COLORMAP -> RGB CONVERSION
214 // Get low 3 bytes from vals[]
217 const char *p;
218 for (y = h-1; y > 0; --y) {
220 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
221 val = vals[(int)*p++];
222 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
223 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
224 *(rgb+0) = val & 0xff; // 0:R
228 return (savergb);
231 unsigned char*
232 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
234 static long vals[256], val;
235 uint32_t t, x, y, colors, cpp;
236 unsigned char c;
237 unsigned char *savergb, *rgb;
238 char transparent;
240 // PARSE HEADER
242 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
243 error << string_compose (_("bad XPM header %1"), xpm[0])
244 << endmsg;
245 return 0;
248 savergb = rgb = (unsigned char*) malloc (h * w * 4);
250 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
252 if (strstr (xpm[1], "None")) {
253 sscanf (xpm[1], "%c", &transparent);
254 t = 1;
255 } else {
256 transparent = 0;
257 t = 0;
260 for (; t < colors; ++t) {
261 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
262 vals[c] = val;
265 // COLORMAP -> RGB CONVERSION
266 // Get low 3 bytes from vals[]
269 const char *p;
270 for (y = h-1; y > 0; --y) {
272 char alpha;
274 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
276 if (transparent && (*p++ == transparent)) {
277 alpha = 0;
278 val = 0;
279 } else {
280 alpha = 255;
281 val = vals[(int)*p];
284 *(rgb+3) = alpha; // 3: alpha
285 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
286 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
287 *(rgb+0) = val & 0xff; // 0:R
291 return (savergb);
294 ArdourCanvas::Points*
295 get_canvas_points (string /*who*/, uint32_t npoints)
297 // cerr << who << ": wants " << npoints << " canvas points" << endl;
298 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
299 if (npoints > (uint32_t) gdk_screen_width() + 4) {
300 abort ();
302 #endif
303 return new ArdourCanvas::Points (npoints);
306 Pango::FontDescription
307 get_font_for_style (string widgetname)
309 Gtk::Window window (WINDOW_TOPLEVEL);
310 Gtk::Label foobar;
311 Glib::RefPtr<Gtk::Style> style;
313 window.add (foobar);
314 foobar.set_name (widgetname);
315 foobar.ensure_style();
317 style = foobar.get_style ();
319 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
321 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
323 if (!pfd) {
325 /* layout inherited its font description from a PangoContext */
327 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
328 pfd = pango_context_get_font_description (ctxt);
329 return Pango::FontDescription (pfd); /* make a copy */
332 return Pango::FontDescription (pfd); /* make a copy */
335 uint32_t
336 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
338 /* In GTK+2, styles aren't set up correctly if the widget is not
339 attached to a toplevel window that has a screen pointer.
342 static Gtk::Window* window = 0;
344 if (window == 0) {
345 window = new Window (WINDOW_TOPLEVEL);
348 Gtk::Label foo;
350 window->add (foo);
352 foo.set_name (style);
353 foo.ensure_style ();
355 GtkRcStyle* rc = foo.get_style()->gobj()->rc_style;
357 if (rc) {
358 if (attr == "fg") {
359 r = rc->fg[state].red / 257;
360 g = rc->fg[state].green / 257;
361 b = rc->fg[state].blue / 257;
363 /* what a hack ... "a" is for "active" */
364 if (state == Gtk::STATE_NORMAL && rgba) {
365 a = rc->fg[GTK_STATE_ACTIVE].red / 257;
367 } else if (attr == "bg") {
368 r = g = b = 0;
369 r = rc->bg[state].red / 257;
370 g = rc->bg[state].green / 257;
371 b = rc->bg[state].blue / 257;
372 } else if (attr == "base") {
373 r = rc->base[state].red / 257;
374 g = rc->base[state].green / 257;
375 b = rc->base[state].blue / 257;
376 } else if (attr == "text") {
377 r = rc->text[state].red / 257;
378 g = rc->text[state].green / 257;
379 b = rc->text[state].blue / 257;
381 } else {
382 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
385 window->remove ();
387 if (state == Gtk::STATE_NORMAL && rgba) {
388 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
389 } else {
390 return (uint32_t) RGB_TO_UINT(r,g,b);
395 Gdk::Color
396 color_from_style (string widget_style_name, int state, string attr)
398 GtkStyle* style;
400 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
401 widget_style_name.c_str(),
402 0, G_TYPE_NONE);
404 if (!style) {
405 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
406 return Gdk::Color ("red");
409 if (attr == "fg") {
410 return Gdk::Color (&style->fg[state]);
413 if (attr == "bg") {
414 return Gdk::Color (&style->bg[state]);
417 if (attr == "light") {
418 return Gdk::Color (&style->light[state]);
421 if (attr == "dark") {
422 return Gdk::Color (&style->dark[state]);
425 if (attr == "mid") {
426 return Gdk::Color (&style->mid[state]);
429 if (attr == "text") {
430 return Gdk::Color (&style->text[state]);
433 if (attr == "base") {
434 return Gdk::Color (&style->base[state]);
437 if (attr == "text_aa") {
438 return Gdk::Color (&style->text_aa[state]);
441 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
442 return Gdk::Color ("red");
445 Glib::RefPtr<Gdk::GC>
446 gc_from_style (string widget_style_name, int state, string attr)
448 GtkStyle* style;
450 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
451 widget_style_name.c_str(),
452 0, G_TYPE_NONE);
454 if (!style) {
455 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
456 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
457 ret->set_rgb_fg_color(Gdk::Color("red"));
458 return ret;
461 if (attr == "fg") {
462 return Glib::wrap(style->fg_gc[state]);
465 if (attr == "bg") {
466 return Glib::wrap(style->bg_gc[state]);
469 if (attr == "light") {
470 return Glib::wrap(style->light_gc[state]);
473 if (attr == "dark") {
474 return Glib::wrap(style->dark_gc[state]);
477 if (attr == "mid") {
478 return Glib::wrap(style->mid_gc[state]);
481 if (attr == "text") {
482 return Glib::wrap(style->text_gc[state]);
485 if (attr == "base") {
486 return Glib::wrap(style->base_gc[state]);
489 if (attr == "text_aa") {
490 return Glib::wrap(style->text_aa_gc[state]);
493 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
494 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
495 ret->set_rgb_fg_color(Gdk::Color("red"));
496 return ret;
500 bool
501 canvas_item_visible (ArdourCanvas::Item* item)
503 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
506 void
507 set_color (Gdk::Color& c, int rgb)
509 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
512 bool
513 relay_key_press (GdkEventKey* ev, Gtk::Window* win)
515 if (!key_press_focus_accelerator_handler (*win, ev)) {
516 return PublicEditor::instance().on_key_press_event(ev);
517 } else {
518 return true;
522 bool
523 forward_key_press (GdkEventKey* ev)
525 return PublicEditor::instance().on_key_press_event(ev);
528 #ifdef GTKOSX
529 static guint
530 osx_keyval_without_alt (guint accent_keyval)
532 switch (accent_keyval) {
533 case GDK_oe:
534 return GDK_q;
535 case GDK_registered:
536 return GDK_r;
537 case GDK_dagger:
538 return GDK_t;
539 case GDK_yen:
540 return GDK_y;
541 case GDK_diaeresis:
542 return GDK_u;
543 case GDK_oslash:
544 return GDK_o;
545 case GDK_Greek_pi:
546 return GDK_p;
547 case GDK_leftdoublequotemark:
548 return GDK_bracketleft;
549 case GDK_leftsinglequotemark:
550 return GDK_bracketright;
551 case GDK_guillemotleft:
552 return GDK_backslash;
553 case GDK_aring:
554 return GDK_a;
555 case GDK_ssharp:
556 return GDK_s;
557 case GDK_partialderivative:
558 return GDK_d;
559 case GDK_function:
560 return GDK_f;
561 case GDK_copyright:
562 return GDK_g;
563 case GDK_abovedot:
564 return GDK_h;
565 case GDK_notsign:
566 return GDK_l;
567 case GDK_ellipsis:
568 return GDK_semicolon;
569 case GDK_ae:
570 return GDK_apostrophe;
571 case GDK_Greek_OMEGA:
572 return GDK_z;
573 case GDK_ccedilla:
574 return GDK_c;
575 case GDK_radical:
576 return GDK_v;
577 case GDK_integral:
578 return GDK_b;
579 case GDK_mu:
580 return GDK_m;
581 case GDK_lessthanequal:
582 return GDK_comma;
583 case GDK_greaterthanequal:
584 return GDK_period;
585 case GDK_division:
586 return GDK_slash;
587 default:
588 break;
591 return GDK_VoidSymbol;
593 #endif
595 bool
596 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
598 GtkWindow* win = window.gobj();
599 GtkWidget* focus = gtk_window_get_focus (win);
600 bool special_handling_of_unmodified_accelerators = false;
601 bool allow_activating = true;
602 /* consider all relevant modifiers but not LOCK or SHIFT */
603 const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
605 if (focus) {
606 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
607 special_handling_of_unmodified_accelerators = true;
611 #ifdef GTKOSX
612 /* should this be universally true? */
613 if (Keyboard::some_magic_widget_has_focus ()) {
614 allow_activating = false;
616 #endif
619 DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 Key event: code = %2 state = %3 special handling ? %4 magic widget focus ? %5 allow_activation ? %6\n",
620 win,
621 ev->keyval,
622 ev->state,
623 special_handling_of_unmodified_accelerators,
624 Keyboard::some_magic_widget_has_focus(),
625 allow_activating));
627 /* This exists to allow us to override the way GTK handles
628 key events. The normal sequence is:
630 a) event is delivered to a GtkWindow
631 b) accelerators/mnemonics are activated
632 c) if (b) didn't handle the event, propagate to
633 the focus widget and/or focus chain
635 The problem with this is that if the accelerators include
636 keys without modifiers, such as the space bar or the
637 letter "e", then pressing the key while typing into
638 a text entry widget results in the accelerator being
639 activated, instead of the desired letter appearing
640 in the text entry.
642 There is no good way of fixing this, but this
643 represents a compromise. The idea is that
644 key events involving modifiers (not Shift)
645 get routed into the activation pathway first, then
646 get propagated to the focus widget if necessary.
648 If the key event doesn't involve modifiers,
649 we deliver to the focus widget first, thus allowing
650 it to get "normal text" without interference
651 from acceleration.
653 Of course, this can also be problematic: if there
654 is a widget with focus, then it will swallow
655 all "normal text" accelerators.
658 #ifdef GTKOSX
659 if (!special_handling_of_unmodified_accelerators && (ev->state & Keyboard::SecondaryModifier)) {
660 /* we're not in a text entry or "magic focus" widget so we don't want OS X "special-character"
661 text-style handling of alt-<key>. change the keyval back to what it would be without
662 the alt key. this way, we see <alt>-v rather than <alt>-radical and so on.
664 guint keyval_without_alt = osx_keyval_without_alt (ev->keyval);
666 if (keyval_without_alt != GDK_VoidSymbol) {
667 DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Remapped %1 to %2\n", gdk_keyval_name (ev->keyval), gdk_keyval_name (keyval_without_alt)));
668 ev->keyval = keyval_without_alt;
672 #endif
674 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
676 /* pretend that certain key events that GTK does not allow
677 to be used as accelerators are actually something that
678 it does allow. but only where there are no modifiers.
681 uint32_t fakekey = ev->keyval;
683 if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
684 if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
685 DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n");
686 return true;
691 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
693 /* no special handling or there are modifiers in effect: accelerate first */
695 DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
697 if (allow_activating) {
698 if (gtk_window_activate_key (win, ev)) {
699 return true;
701 } else {
702 DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
705 DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
707 return gtk_window_propagate_key_event (win, ev);
710 /* no modifiers, propagate first */
712 DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
714 if (!gtk_window_propagate_key_event (win, ev)) {
715 DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
716 if (allow_activating) {
717 return gtk_window_activate_key (win, ev);
718 } else {
719 DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
722 } else {
723 DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
724 return true;
727 DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
728 return true;
731 Glib::RefPtr<Gdk::Pixbuf>
732 get_xpm (std::string name)
734 if (!xpm_map[name]) {
736 SearchPath spath(ARDOUR::ardour_search_path());
737 spath += ARDOUR::system_data_search_path();
739 spath.add_subdirectory_to_paths("pixmaps");
741 sys::path data_file_path;
743 if(!find_file_in_search_path (spath, name, data_file_path)) {
744 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
747 try {
748 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
749 } catch(const Glib::Error& e) {
750 warning << "Caught Glib::Error: " << e.what() << endmsg;
754 return xpm_map[name];
757 std::string
758 get_icon_path (const char* cname)
760 string name = cname;
761 name += X_(".png");
763 SearchPath spath(ARDOUR::ardour_search_path());
764 spath += ARDOUR::system_data_search_path();
766 spath.add_subdirectory_to_paths("icons");
768 sys::path data_file_path;
770 if (!find_file_in_search_path (spath, name, data_file_path)) {
771 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
774 return data_file_path.to_string();
777 Glib::RefPtr<Gdk::Pixbuf>
778 get_icon (const char* cname)
780 Glib::RefPtr<Gdk::Pixbuf> img;
781 try {
782 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
783 } catch (const Gdk::PixbufError &e) {
784 cerr << "Caught PixbufError: " << e.what() << endl;
785 } catch (...) {
786 g_message("Caught ... ");
789 return img;
792 string
793 longest (vector<string>& strings)
795 if (strings.empty()) {
796 return string ("");
799 vector<string>::iterator longest = strings.begin();
800 string::size_type longest_length = (*longest).length();
802 vector<string>::iterator i = longest;
803 ++i;
805 while (i != strings.end()) {
807 string::size_type len = (*i).length();
809 if (len > longest_length) {
810 longest = i;
811 longest_length = len;
814 ++i;
817 return *longest;
820 bool
821 key_is_legal_for_numeric_entry (guint keyval)
823 switch (keyval) {
824 case GDK_minus:
825 case GDK_plus:
826 case GDK_period:
827 case GDK_comma:
828 case GDK_0:
829 case GDK_1:
830 case GDK_2:
831 case GDK_3:
832 case GDK_4:
833 case GDK_5:
834 case GDK_6:
835 case GDK_7:
836 case GDK_8:
837 case GDK_9:
838 case GDK_KP_Add:
839 case GDK_KP_Subtract:
840 case GDK_KP_Decimal:
841 case GDK_KP_0:
842 case GDK_KP_1:
843 case GDK_KP_2:
844 case GDK_KP_3:
845 case GDK_KP_4:
846 case GDK_KP_5:
847 case GDK_KP_6:
848 case GDK_KP_7:
849 case GDK_KP_8:
850 case GDK_KP_9:
851 case GDK_Return:
852 case GDK_BackSpace:
853 case GDK_Delete:
854 case GDK_KP_Enter:
855 case GDK_Home:
856 case GDK_End:
857 case GDK_Left:
858 case GDK_Right:
859 return true;
861 default:
862 break;
865 return false;
867 void
868 set_pango_fontsize ()
870 long val = ARDOUR::Config->get_font_scale();
872 /* FT2 rendering - used by GnomeCanvas, sigh */
874 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
876 /* Cairo rendering, in case there is any */
878 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
881 void
882 reset_dpi ()
884 long val = ARDOUR::Config->get_font_scale();
885 set_pango_fontsize ();
886 /* Xft rendering */
888 gtk_settings_set_long_property (gtk_settings_get_default(),
889 "gtk-xft-dpi", val, "ardour");
890 DPIReset();//Emit Signal
893 void
894 resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
896 Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
897 Gdk::Rectangle monitor_rect;
898 screen->get_monitor_geometry (0, monitor_rect);
900 int const w = std::min (int (monitor_rect.get_width() * 0.8), max_width);
901 int const h = std::min (int (monitor_rect.get_height() * 0.8), max_height);
903 window->resize (w, h);
907 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
908 string
909 escape_underscores (string const & s)
911 string o;
912 string::size_type const N = s.length ();
914 for (string::size_type i = 0; i < N; ++i) {
915 if (s[i] == '_') {
916 o += "__";
917 } else {
918 o += s[i];
922 return o;