fix rounding errors and bbox glitches that led to lines missing redraws, plus a few...
[ardour2.git] / gtk2_ardour / utils.cc
blob621cdb0dd9a9e90827aa801178b2e252a6cd9abf
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 "public_editor.h"
49 #include "keyboard.h"
50 #include "utils.h"
51 #include "i18n.h"
52 #include "rgb_macros.h"
53 #include "canvas_impl.h"
54 #include "gui_thread.h"
56 using namespace std;
57 using namespace Gtk;
58 using namespace Glib;
59 using namespace PBD;
60 using Gtkmm2ext::Keyboard;
62 sigc::signal<void> DPIReset;
64 int
65 pixel_width (const string& str, Pango::FontDescription& font)
67 Label foo;
68 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
70 layout->set_font_description (font);
71 layout->set_text (str);
73 int width, height;
74 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
75 return width;
78 string
79 fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
81 Label foo;
82 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
83 string::size_type shorter_by = 0;
84 string txt;
86 layout->set_font_description (font);
88 actual_width = 0;
90 string ustr = str;
91 string::iterator last = ustr.end();
92 --last; /* now points at final entry */
94 txt = ustr;
96 while (!ustr.empty()) {
98 layout->set_text (txt);
100 int width, height;
101 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
103 if (width < pixel_width) {
104 actual_width = width;
105 break;
108 ustr.erase (last--);
109 shorter_by++;
111 if (with_ellipses && shorter_by > 3) {
112 txt = ustr;
113 txt += "...";
114 } else {
115 txt = ustr;
119 return txt;
122 /** Try to fit a string into a given horizontal space by ellipsizing it.
123 * @param cr Cairo context in which the text will be plotted.
124 * @param name Text.
125 * @param avail Available horizontal space.
126 * @return (Text, possibly ellipsized) and (horizontal size of text)
129 std::pair<std::string, double>
130 fit_to_pixels (cairo_t* cr, std::string name, double avail)
132 /* XXX hopefully there exists a more efficient way of doing this */
134 bool abbreviated = false;
135 uint32_t width = 0;
137 while (1) {
138 cairo_text_extents_t ext;
139 cairo_text_extents (cr, name.c_str(), &ext);
141 if (ext.width < avail || name.length() <= 4) {
142 width = ext.width;
143 break;
146 if (abbreviated) {
147 name = name.substr (0, name.length() - 4) + "...";
148 } else {
149 name = name.substr (0, name.length() - 3) + "...";
150 abbreviated = true;
154 return std::make_pair (name, width);
158 /** Add an element to a menu, settings its sensitivity.
159 * @param m Menu to add to.
160 * @param e Element to add.
161 * @param s true to make sensitive, false to make insensitive
163 void
164 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
166 m.push_back (e);
167 if (!s) {
168 m.back().set_sensitive (false);
173 gint
174 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
176 win->hide ();
177 return 0;
180 /* xpm2rgb copied from nixieclock, which bore the legend:
182 nixieclock - a nixie desktop timepiece
183 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
185 and was released under the GPL.
188 unsigned char*
189 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
191 static long vals[256], val;
192 uint32_t t, x, y, colors, cpp;
193 unsigned char c;
194 unsigned char *savergb, *rgb;
196 // PARSE HEADER
198 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
199 error << string_compose (_("bad XPM header %1"), xpm[0])
200 << endmsg;
201 return 0;
204 savergb = rgb = (unsigned char*) malloc (h * w * 3);
206 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
207 for (t = 0; t < colors; ++t) {
208 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
209 vals[c] = val;
212 // COLORMAP -> RGB CONVERSION
213 // Get low 3 bytes from vals[]
216 const char *p;
217 for (y = h-1; y > 0; --y) {
219 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
220 val = vals[(int)*p++];
221 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
222 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
223 *(rgb+0) = val & 0xff; // 0:R
227 return (savergb);
230 unsigned char*
231 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
233 static long vals[256], val;
234 uint32_t t, x, y, colors, cpp;
235 unsigned char c;
236 unsigned char *savergb, *rgb;
237 char transparent;
239 // PARSE HEADER
241 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
242 error << string_compose (_("bad XPM header %1"), xpm[0])
243 << endmsg;
244 return 0;
247 savergb = rgb = (unsigned char*) malloc (h * w * 4);
249 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
251 if (strstr (xpm[1], "None")) {
252 sscanf (xpm[1], "%c", &transparent);
253 t = 1;
254 } else {
255 transparent = 0;
256 t = 0;
259 for (; t < colors; ++t) {
260 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
261 vals[c] = val;
264 // COLORMAP -> RGB CONVERSION
265 // Get low 3 bytes from vals[]
268 const char *p;
269 for (y = h-1; y > 0; --y) {
271 char alpha;
273 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
275 if (transparent && (*p++ == transparent)) {
276 alpha = 0;
277 val = 0;
278 } else {
279 alpha = 255;
280 val = vals[(int)*p];
283 *(rgb+3) = alpha; // 3: alpha
284 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
285 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
286 *(rgb+0) = val & 0xff; // 0:R
290 return (savergb);
293 ArdourCanvas::Points*
294 get_canvas_points (string /*who*/, uint32_t npoints)
296 // cerr << who << ": wants " << npoints << " canvas points" << endl;
297 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
298 if (npoints > (uint32_t) gdk_screen_width() + 4) {
299 abort ();
301 #endif
302 return new ArdourCanvas::Points (npoints);
305 Pango::FontDescription*
306 get_font_for_style (string widgetname)
308 Gtk::Window window (WINDOW_TOPLEVEL);
309 Gtk::Label foobar;
310 Glib::RefPtr<Gtk::Style> style;
312 window.add (foobar);
313 foobar.set_name (widgetname);
314 foobar.ensure_style();
316 style = foobar.get_style ();
318 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
320 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
322 if (!pfd) {
324 /* layout inherited its font description from a PangoContext */
326 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
327 pfd = pango_context_get_font_description (ctxt);
328 return new Pango::FontDescription (pfd, true); /* make a copy */
331 return new Pango::FontDescription (pfd, true); /* make a copy */
334 uint32_t
335 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
337 /* In GTK+2, styles aren't set up correctly if the widget is not
338 attached to a toplevel window that has a screen pointer.
341 static Gtk::Window* window = 0;
343 if (window == 0) {
344 window = new Window (WINDOW_TOPLEVEL);
347 Gtk::Label foo;
349 window->add (foo);
351 foo.set_name (style);
352 foo.ensure_style ();
354 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
356 if (waverc) {
357 if (attr == "fg") {
358 r = waverc->fg[state].red / 257;
359 g = waverc->fg[state].green / 257;
360 b = waverc->fg[state].blue / 257;
362 /* what a hack ... "a" is for "active" */
363 if (state == Gtk::STATE_NORMAL && rgba) {
364 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
366 } else if (attr == "bg") {
367 r = g = b = 0;
368 r = waverc->bg[state].red / 257;
369 g = waverc->bg[state].green / 257;
370 b = waverc->bg[state].blue / 257;
371 } else if (attr == "base") {
372 r = waverc->base[state].red / 257;
373 g = waverc->base[state].green / 257;
374 b = waverc->base[state].blue / 257;
375 } else if (attr == "text") {
376 r = waverc->text[state].red / 257;
377 g = waverc->text[state].green / 257;
378 b = waverc->text[state].blue / 257;
380 } else {
381 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
384 window->remove ();
386 if (state == Gtk::STATE_NORMAL && rgba) {
387 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
388 } else {
389 return (uint32_t) RGB_TO_UINT(r,g,b);
394 Gdk::Color
395 color_from_style (string widget_style_name, int state, string attr)
397 GtkStyle* style;
399 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
400 widget_style_name.c_str(),
401 0, G_TYPE_NONE);
403 if (!style) {
404 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
405 return Gdk::Color ("red");
408 if (attr == "fg") {
409 return Gdk::Color (&style->fg[state]);
412 if (attr == "bg") {
413 return Gdk::Color (&style->bg[state]);
416 if (attr == "light") {
417 return Gdk::Color (&style->light[state]);
420 if (attr == "dark") {
421 return Gdk::Color (&style->dark[state]);
424 if (attr == "mid") {
425 return Gdk::Color (&style->mid[state]);
428 if (attr == "text") {
429 return Gdk::Color (&style->text[state]);
432 if (attr == "base") {
433 return Gdk::Color (&style->base[state]);
436 if (attr == "text_aa") {
437 return Gdk::Color (&style->text_aa[state]);
440 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
441 return Gdk::Color ("red");
444 Glib::RefPtr<Gdk::GC>
445 gc_from_style (string widget_style_name, int state, string attr)
447 GtkStyle* style;
449 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
450 widget_style_name.c_str(),
451 0, G_TYPE_NONE);
453 if (!style) {
454 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
455 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
456 ret->set_rgb_fg_color(Gdk::Color("red"));
457 return ret;
460 if (attr == "fg") {
461 return Glib::wrap(style->fg_gc[state]);
464 if (attr == "bg") {
465 return Glib::wrap(style->bg_gc[state]);
468 if (attr == "light") {
469 return Glib::wrap(style->light_gc[state]);
472 if (attr == "dark") {
473 return Glib::wrap(style->dark_gc[state]);
476 if (attr == "mid") {
477 return Glib::wrap(style->mid_gc[state]);
480 if (attr == "text") {
481 return Glib::wrap(style->text_gc[state]);
484 if (attr == "base") {
485 return Glib::wrap(style->base_gc[state]);
488 if (attr == "text_aa") {
489 return Glib::wrap(style->text_aa_gc[state]);
492 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
493 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
494 ret->set_rgb_fg_color(Gdk::Color("red"));
495 return ret;
499 bool
500 canvas_item_visible (ArdourCanvas::Item* item)
502 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
505 void
506 set_color (Gdk::Color& c, int rgb)
508 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
511 bool
512 relay_key_press (GdkEventKey* ev, Gtk::Window* win)
514 if (!key_press_focus_accelerator_handler (*win, ev)) {
515 return PublicEditor::instance().on_key_press_event(ev);
516 } else {
517 return true;
521 bool
522 forward_key_press (GdkEventKey* ev)
524 return PublicEditor::instance().on_key_press_event(ev);
527 #ifdef GTKOSX
528 static guint
529 osx_keyval_without_alt (guint accent_keyval)
531 switch (accent_keyval) {
532 case GDK_oe:
533 return GDK_q;
534 case GDK_registered:
535 return GDK_r;
536 case GDK_dagger:
537 return GDK_t;
538 case GDK_yen:
539 return GDK_y;
540 case GDK_diaeresis:
541 return GDK_u;
542 case GDK_oslash:
543 return GDK_o;
544 case GDK_Greek_pi:
545 return GDK_p;
546 case GDK_leftdoublequotemark:
547 return GDK_bracketleft;
548 case GDK_leftsinglequotemark:
549 return GDK_bracketright;
550 case GDK_guillemotleft:
551 return GDK_backslash;
552 case GDK_aring:
553 return GDK_a;
554 case GDK_ssharp:
555 return GDK_s;
556 case GDK_partialderivative:
557 return GDK_d;
558 case GDK_function:
559 return GDK_f;
560 case GDK_copyright:
561 return GDK_g;
562 case GDK_abovedot:
563 return GDK_h;
564 case GDK_notsign:
565 return GDK_l;
566 case GDK_ellipsis:
567 return GDK_semicolon;
568 case GDK_ae:
569 return GDK_apostrophe;
570 case GDK_Greek_OMEGA:
571 return GDK_z;
572 case GDK_ccedilla:
573 return GDK_c;
574 case GDK_radical:
575 return GDK_v;
576 case GDK_integral:
577 return GDK_b;
578 case GDK_mu:
579 return GDK_m;
580 case GDK_lessthanequal:
581 return GDK_comma;
582 case GDK_greaterthanequal:
583 return GDK_period;
584 case GDK_division:
585 return GDK_slash;
586 default:
587 break;
590 return GDK_VoidSymbol;
592 #endif
594 bool
595 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
597 GtkWindow* win = window.gobj();
598 GtkWidget* focus = gtk_window_get_focus (win);
599 bool special_handling_of_unmodified_accelerators = false;
600 bool allow_activating = true;
602 #undef DEBUG_ACCELERATOR_HANDLING
603 #ifdef DEBUG_ACCELERATOR_HANDLING
604 //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
605 bool debug=true;
606 #endif
607 if (focus) {
608 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
609 special_handling_of_unmodified_accelerators = true;
613 #ifdef GTKOSX
614 /* should this be universally true? */
615 if (Keyboard::some_magic_widget_has_focus ()) {
616 allow_activating = false;
618 #endif
620 #ifdef DEBUG_ACCELERATOR_HANDLING
621 if (debug) {
622 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
623 << special_handling_of_unmodified_accelerators
624 << " magic widget focus ? "
625 << Keyboard::some_magic_widget_has_focus()
626 << " allow_activation ? "
627 << allow_activating
628 << endl;
630 #endif
632 /* This exists to allow us to override the way GTK handles
633 key events. The normal sequence is:
635 a) event is delivered to a GtkWindow
636 b) accelerators/mnemonics are activated
637 c) if (b) didn't handle the event, propagate to
638 the focus widget and/or focus chain
640 The problem with this is that if the accelerators include
641 keys without modifiers, such as the space bar or the
642 letter "e", then pressing the key while typing into
643 a text entry widget results in the accelerator being
644 activated, instead of the desired letter appearing
645 in the text entry.
647 There is no good way of fixing this, but this
648 represents a compromise. The idea is that
649 key events involving modifiers (not Shift)
650 get routed into the activation pathway first, then
651 get propagated to the focus widget if necessary.
653 If the key event doesn't involve modifiers,
654 we deliver to the focus widget first, thus allowing
655 it to get "normal text" without interference
656 from acceleration.
658 Of course, this can also be problematic: if there
659 is a widget with focus, then it will swallow
660 all "normal text" accelerators.
663 #ifdef GTKOSX
664 if (!special_handling_of_unmodified_accelerators) {
665 if (ev->state & GDK_MOD1_MASK) {
666 /* we're not in a text entry or "magic focus" widget so we don't want OS X "special-character"
667 text-style handling of alt-<key>. change the keyval back to what it would be without
668 the alt key. this way, we see <alt>-v rather than <alt>-radical and so on.
670 guint keyval_without_alt = osx_keyval_without_alt (ev->keyval);
672 if (keyval_without_alt != GDK_VoidSymbol) {
673 #ifdef DEBUG_ACCELERATOR_HANDLING
674 cerr << "Remapped " << gdk_keyval_name (ev->keyval) << " to " << gdk_keyval_name (keyval_without_alt) << endl;
676 #endif ev->keyval = keyval_without_alt;
680 #endif
682 if (!special_handling_of_unmodified_accelerators) {
684 /* pretend that certain key events that GTK does not allow
685 to be used as accelerators are actually something that
686 it does allow.
689 uint32_t fakekey = ev->keyval;
691 if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
692 if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
693 return true;
698 /* consider all relevant modifiers but not LOCK or SHIFT */
700 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
702 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
704 /* no special handling or there are modifiers in effect: accelerate first */
706 #ifdef DEBUG_ACCELERATOR_HANDLING
707 if (debug) {
708 cerr << "\tactivate, then propagate\n";
710 #endif
712 if (allow_activating) {
713 if (gtk_window_activate_key (win, ev)) {
714 return true;
718 #ifdef DEBUG_ACCELERATOR_HANDLING
719 if (debug) {
720 cerr << "\tnot accelerated, now propagate\n";
722 #endif
723 return gtk_window_propagate_key_event (win, ev);
726 /* no modifiers, propagate first */
728 #ifdef DEBUG_ACCELERATOR_HANDLING
729 if (debug) {
730 cerr << "\tpropagate, then activate\n";
732 #endif
733 if (!gtk_window_propagate_key_event (win, ev)) {
734 #ifdef DEBUG_ACCELERATOR_HANDLING
735 if (debug) {
736 cerr << "\tpropagation didn't handle, so activate\n";
738 #endif
740 if (allow_activating) {
741 return gtk_window_activate_key (win, ev);
744 } else {
745 #ifdef DEBUG_ACCELERATOR_HANDLING
746 if (debug) {
747 cerr << "\thandled by propagate\n";
749 #endif
750 return true;
753 #ifdef DEBUG_ACCELERATOR_HANDLING
754 if (debug) {
755 cerr << "\tnot handled\n";
757 #endif
758 return true;
761 Glib::RefPtr<Gdk::Pixbuf>
762 get_xpm (std::string name)
764 if (!xpm_map[name]) {
766 SearchPath spath(ARDOUR::ardour_search_path());
767 spath += ARDOUR::system_data_search_path();
769 spath.add_subdirectory_to_paths("pixmaps");
771 sys::path data_file_path;
773 if(!find_file_in_search_path (spath, name, data_file_path)) {
774 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
777 try {
778 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
779 } catch(const Glib::Error& e) {
780 warning << "Caught Glib::Error: " << e.what() << endmsg;
784 return xpm_map[name];
787 std::string
788 get_icon_path (const char* cname)
790 string name = cname;
791 name += X_(".png");
793 SearchPath spath(ARDOUR::ardour_search_path());
794 spath += ARDOUR::system_data_search_path();
796 spath.add_subdirectory_to_paths("icons");
798 sys::path data_file_path;
800 if (!find_file_in_search_path (spath, name, data_file_path)) {
801 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
804 return data_file_path.to_string();
807 Glib::RefPtr<Gdk::Pixbuf>
808 get_icon (const char* cname)
810 Glib::RefPtr<Gdk::Pixbuf> img;
811 try {
812 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
813 } catch (const Gdk::PixbufError &e) {
814 cerr << "Caught PixbufError: " << e.what() << endl;
815 } catch (...) {
816 g_message("Caught ... ");
819 return img;
822 string
823 longest (vector<string>& strings)
825 if (strings.empty()) {
826 return string ("");
829 vector<string>::iterator longest = strings.begin();
830 string::size_type longest_length = (*longest).length();
832 vector<string>::iterator i = longest;
833 ++i;
835 while (i != strings.end()) {
837 string::size_type len = (*i).length();
839 if (len > longest_length) {
840 longest = i;
841 longest_length = len;
844 ++i;
847 return *longest;
850 bool
851 key_is_legal_for_numeric_entry (guint keyval)
853 switch (keyval) {
854 case GDK_minus:
855 case GDK_plus:
856 case GDK_period:
857 case GDK_comma:
858 case GDK_0:
859 case GDK_1:
860 case GDK_2:
861 case GDK_3:
862 case GDK_4:
863 case GDK_5:
864 case GDK_6:
865 case GDK_7:
866 case GDK_8:
867 case GDK_9:
868 case GDK_KP_Add:
869 case GDK_KP_Subtract:
870 case GDK_KP_Decimal:
871 case GDK_KP_0:
872 case GDK_KP_1:
873 case GDK_KP_2:
874 case GDK_KP_3:
875 case GDK_KP_4:
876 case GDK_KP_5:
877 case GDK_KP_6:
878 case GDK_KP_7:
879 case GDK_KP_8:
880 case GDK_KP_9:
881 case GDK_Return:
882 case GDK_BackSpace:
883 case GDK_Delete:
884 case GDK_KP_Enter:
885 case GDK_Home:
886 case GDK_End:
887 case GDK_Left:
888 case GDK_Right:
889 return true;
891 default:
892 break;
895 return false;
897 void
898 set_pango_fontsize ()
900 long val = ARDOUR::Config->get_font_scale();
902 /* FT2 rendering - used by GnomeCanvas, sigh */
904 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
906 /* Cairo rendering, in case there is any */
908 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
911 void
912 reset_dpi ()
914 long val = ARDOUR::Config->get_font_scale();
915 set_pango_fontsize ();
916 /* Xft rendering */
918 gtk_settings_set_long_property (gtk_settings_get_default(),
919 "gtk-xft-dpi", val, "ardour");
920 DPIReset();//Emit Signal
923 void
924 resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
926 Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
927 Gdk::Rectangle monitor_rect;
928 screen->get_monitor_geometry (0, monitor_rect);
930 int const w = std::min (int (monitor_rect.get_width() * 0.8), max_width);
931 int const h = std::min (int (monitor_rect.get_height() * 0.8), max_height);
933 window->resize (w, h);
937 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
938 string
939 escape_underscores (string const & s)
941 string o;
942 string::size_type const N = s.length ();
944 for (string::size_type i = 0; i < N; ++i) {
945 if (s[i] == '_') {
946 o += "__";
947 } else {
948 o += s[i];
952 return o;
955 static void
956 adjustment_to_controllable (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
958 boost::shared_ptr<Controllable> cont = wcont.lock();
960 if (cont) {
961 double val = adj->get_value();
962 if (val != cont->get_value()) {
963 cont->set_value (val);
968 static void
969 controllable_to_adjustment (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
971 boost::shared_ptr<Controllable> cont = wcont.lock();
973 if (cont) {
974 float val = cont->get_value();
976 if (val != adj->get_value()) {
977 adj->set_value (val);
982 void
983 control_link (ScopedConnectionList& scl, boost::shared_ptr<Controllable> c, Gtk::Adjustment& a)
985 boost::weak_ptr<Controllable> wc (c);
987 a.signal_value_changed().connect (sigc::bind (sigc::ptr_fun (adjustment_to_controllable), &a, wc));
988 c->Changed.connect (scl, MISSING_INVALIDATOR, boost::bind (controllable_to_adjustment, &a, wc),
989 gui_context());