fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / utils.cc
blobac43d06e5984d66c5f41679db528b1e251aa2ce5
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 #include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
21 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
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>
35 #include "pbd/file_utils.h"
37 #include <gtkmm2ext/utils.h>
38 #include "ardour/configuration.h"
39 #include "ardour/rc_configuration.h"
41 #include "ardour/filesystem_paths.h"
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 #include "gui_thread.h"
52 using namespace std;
53 using namespace Gtk;
54 using namespace Glib;
55 using namespace PBD;
56 using Gtkmm2ext::Keyboard;
58 sigc::signal<void> DPIReset;
60 int
61 pixel_width (const ustring& str, Pango::FontDescription& font)
63 Label foo;
64 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
66 layout->set_font_description (font);
67 layout->set_text (str);
69 int width, height;
70 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
71 return width;
74 ustring
75 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
77 Label foo;
78 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
79 ustring::size_type shorter_by = 0;
80 ustring txt;
82 layout->set_font_description (font);
84 actual_width = 0;
86 ustring ustr = str;
87 ustring::iterator last = ustr.end();
88 --last; /* now points at final entry */
90 txt = ustr;
92 while (!ustr.empty()) {
94 layout->set_text (txt);
96 int width, height;
97 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
99 if (width < pixel_width) {
100 actual_width = width;
101 break;
104 ustr.erase (last--);
105 shorter_by++;
107 if (with_ellipses && shorter_by > 3) {
108 txt = ustr;
109 txt += "...";
110 } else {
111 txt = ustr;
115 return txt;
118 /** Try to fit a string into a given horizontal space by ellipsizing it.
119 * @param cr Cairo context in which the text will be plotted.
120 * @param name Text.
121 * @param avail Available horizontal space.
122 * @return (Text, possibly ellipsized) and (horizontal size of text)
125 std::pair<std::string, double>
126 fit_to_pixels (cairo_t* cr, std::string name, double avail)
128 /* XXX hopefully there exists a more efficient way of doing this */
130 bool abbreviated = false;
131 uint32_t width = 0;
133 while (1) {
134 cairo_text_extents_t ext;
135 cairo_text_extents (cr, name.c_str(), &ext);
137 if (ext.width < avail || name.length() <= 4) {
138 width = ext.width;
139 break;
142 if (abbreviated) {
143 name = name.substr (0, name.length() - 4) + "...";
144 } else {
145 name = name.substr (0, name.length() - 3) + "...";
146 abbreviated = true;
150 return std::make_pair (name, width);
154 /** Add an element to a menu, settings its sensitivity.
155 * @param m Menu to add to.
156 * @param e Element to add.
157 * @param s true to make sensitive, false to make insensitive
159 void
160 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
162 m.push_back (e);
163 if (!s) {
164 m.back().set_sensitive (false);
169 gint
170 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
172 win->hide ();
173 return 0;
176 /* xpm2rgb copied from nixieclock, which bore the legend:
178 nixieclock - a nixie desktop timepiece
179 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
181 and was released under the GPL.
184 unsigned char*
185 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
187 static long vals[256], val;
188 uint32_t t, x, y, colors, cpp;
189 unsigned char c;
190 unsigned char *savergb, *rgb;
192 // PARSE HEADER
194 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
195 error << string_compose (_("bad XPM header %1"), xpm[0])
196 << endmsg;
197 return 0;
200 savergb = rgb = (unsigned char*) malloc (h * w * 3);
202 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
203 for (t = 0; t < colors; ++t) {
204 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
205 vals[c] = val;
208 // COLORMAP -> RGB CONVERSION
209 // Get low 3 bytes from vals[]
212 const char *p;
213 for (y = h-1; y > 0; --y) {
215 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
216 val = vals[(int)*p++];
217 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
218 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
219 *(rgb+0) = val & 0xff; // 0:R
223 return (savergb);
226 unsigned char*
227 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
229 static long vals[256], val;
230 uint32_t t, x, y, colors, cpp;
231 unsigned char c;
232 unsigned char *savergb, *rgb;
233 char transparent;
235 // PARSE HEADER
237 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
238 error << string_compose (_("bad XPM header %1"), xpm[0])
239 << endmsg;
240 return 0;
243 savergb = rgb = (unsigned char*) malloc (h * w * 4);
245 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
247 if (strstr (xpm[1], "None")) {
248 sscanf (xpm[1], "%c", &transparent);
249 t = 1;
250 } else {
251 transparent = 0;
252 t = 0;
255 for (; t < colors; ++t) {
256 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
257 vals[c] = val;
260 // COLORMAP -> RGB CONVERSION
261 // Get low 3 bytes from vals[]
264 const char *p;
265 for (y = h-1; y > 0; --y) {
267 char alpha;
269 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
271 if (transparent && (*p++ == transparent)) {
272 alpha = 0;
273 val = 0;
274 } else {
275 alpha = 255;
276 val = vals[(int)*p];
279 *(rgb+3) = alpha; // 3: alpha
280 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
281 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
282 *(rgb+0) = val & 0xff; // 0:R
286 return (savergb);
289 ArdourCanvas::Points*
290 get_canvas_points (string /*who*/, uint32_t npoints)
292 // cerr << who << ": wants " << npoints << " canvas points" << endl;
293 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
294 if (npoints > (uint32_t) gdk_screen_width() + 4) {
295 abort ();
297 #endif
298 return new ArdourCanvas::Points (npoints);
301 Pango::FontDescription*
302 get_font_for_style (string widgetname)
304 Gtk::Window window (WINDOW_TOPLEVEL);
305 Gtk::Label foobar;
306 Glib::RefPtr<Gtk::Style> style;
308 window.add (foobar);
309 foobar.set_name (widgetname);
310 foobar.ensure_style();
312 style = foobar.get_style ();
314 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
316 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
318 if (!pfd) {
320 /* layout inherited its font description from a PangoContext */
322 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
323 pfd = pango_context_get_font_description (ctxt);
324 return new Pango::FontDescription (pfd, true); /* make a copy */
327 return new Pango::FontDescription (pfd, true); /* make a copy */
330 uint32_t
331 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
333 /* In GTK+2, styles aren't set up correctly if the widget is not
334 attached to a toplevel window that has a screen pointer.
337 static Gtk::Window* window = 0;
339 if (window == 0) {
340 window = new Window (WINDOW_TOPLEVEL);
343 Gtk::Label foo;
345 window->add (foo);
347 foo.set_name (style);
348 foo.ensure_style ();
350 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
352 if (waverc) {
353 if (attr == "fg") {
354 r = waverc->fg[state].red / 257;
355 g = waverc->fg[state].green / 257;
356 b = waverc->fg[state].blue / 257;
358 /* what a hack ... "a" is for "active" */
359 if (state == Gtk::STATE_NORMAL && rgba) {
360 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
362 } else if (attr == "bg") {
363 r = g = b = 0;
364 r = waverc->bg[state].red / 257;
365 g = waverc->bg[state].green / 257;
366 b = waverc->bg[state].blue / 257;
367 } else if (attr == "base") {
368 r = waverc->base[state].red / 257;
369 g = waverc->base[state].green / 257;
370 b = waverc->base[state].blue / 257;
371 } else if (attr == "text") {
372 r = waverc->text[state].red / 257;
373 g = waverc->text[state].green / 257;
374 b = waverc->text[state].blue / 257;
376 } else {
377 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
380 window->remove ();
382 if (state == Gtk::STATE_NORMAL && rgba) {
383 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
384 } else {
385 return (uint32_t) RGB_TO_UINT(r,g,b);
390 Gdk::Color
391 color_from_style (string widget_style_name, int state, string attr)
393 GtkStyle* style;
395 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
396 widget_style_name.c_str(),
397 0, G_TYPE_NONE);
399 if (!style) {
400 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
401 return Gdk::Color ("red");
404 if (attr == "fg") {
405 return Gdk::Color (&style->fg[state]);
408 if (attr == "bg") {
409 return Gdk::Color (&style->bg[state]);
412 if (attr == "light") {
413 return Gdk::Color (&style->light[state]);
416 if (attr == "dark") {
417 return Gdk::Color (&style->dark[state]);
420 if (attr == "mid") {
421 return Gdk::Color (&style->mid[state]);
424 if (attr == "text") {
425 return Gdk::Color (&style->text[state]);
428 if (attr == "base") {
429 return Gdk::Color (&style->base[state]);
432 if (attr == "text_aa") {
433 return Gdk::Color (&style->text_aa[state]);
436 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
437 return Gdk::Color ("red");
440 Glib::RefPtr<Gdk::GC>
441 gc_from_style (string widget_style_name, int state, string attr)
443 GtkStyle* style;
445 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
446 widget_style_name.c_str(),
447 0, G_TYPE_NONE);
449 if (!style) {
450 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
451 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
452 ret->set_rgb_fg_color(Gdk::Color("red"));
453 return ret;
456 if (attr == "fg") {
457 return Glib::wrap(style->fg_gc[state]);
460 if (attr == "bg") {
461 return Glib::wrap(style->bg_gc[state]);
464 if (attr == "light") {
465 return Glib::wrap(style->light_gc[state]);
468 if (attr == "dark") {
469 return Glib::wrap(style->dark_gc[state]);
472 if (attr == "mid") {
473 return Glib::wrap(style->mid_gc[state]);
476 if (attr == "text") {
477 return Glib::wrap(style->text_gc[state]);
480 if (attr == "base") {
481 return Glib::wrap(style->base_gc[state]);
484 if (attr == "text_aa") {
485 return Glib::wrap(style->text_aa_gc[state]);
488 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
489 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
490 ret->set_rgb_fg_color(Gdk::Color("red"));
491 return ret;
495 bool
496 canvas_item_visible (ArdourCanvas::Item* item)
498 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
501 void
502 set_color (Gdk::Color& c, int rgb)
504 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
507 bool
508 relay_key_press (GdkEventKey* ev, Gtk::Window* win)
510 if (!key_press_focus_accelerator_handler (*win, ev)) {
511 return PublicEditor::instance().on_key_press_event(ev);
512 } else {
513 return true;
517 bool
518 forward_key_press (GdkEventKey* ev)
520 return PublicEditor::instance().on_key_press_event(ev);
523 #ifdef GTKOSX
524 static guint
525 osx_keyval_without_alt (guint accent_keyval)
527 switch (accent_keyval) {
528 case GDK_oe:
529 return GDK_q;
530 case GDK_registered:
531 return GDK_r;
532 case GDK_dagger:
533 return GDK_t;
534 case GDK_yen:
535 return GDK_y;
536 case GDK_diaeresis:
537 return GDK_u;
538 case GDK_oslash:
539 return GDK_o;
540 case GDK_Greek_pi:
541 return GDK_p;
542 case GDK_leftdoublequotemark:
543 return GDK_bracketleft;
544 case GDK_leftsinglequotemark:
545 return GDK_bracketright;
546 case GDK_guillemotleft:
547 return GDK_backslash;
548 case GDK_aring:
549 return GDK_a;
550 case GDK_ssharp:
551 return GDK_s;
552 case GDK_partialderivative:
553 return GDK_d;
554 case GDK_function:
555 return GDK_f;
556 case GDK_copyright:
557 return GDK_g;
558 case GDK_abovedot:
559 return GDK_h;
560 case GDK_notsign:
561 return GDK_l;
562 case GDK_ellipsis:
563 return GDK_semicolon;
564 case GDK_ae:
565 return GDK_apostrophe;
566 case GDK_Greek_OMEGA:
567 return GDK_z;
568 case GDK_ccedilla:
569 return GDK_c;
570 case GDK_radical:
571 return GDK_v;
572 case GDK_integral:
573 return GDK_b;
574 case GDK_mu:
575 return GDK_m;
576 case GDK_lessthanequal:
577 return GDK_comma;
578 case GDK_greaterthanequal:
579 return GDK_period;
580 case GDK_division:
581 return GDK_slash;
582 default:
583 break;
586 return GDK_VoidSymbol;
588 #endif
590 bool
591 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
593 GtkWindow* win = window.gobj();
594 GtkWidget* focus = gtk_window_get_focus (win);
595 bool special_handling_of_unmodified_accelerators = false;
596 bool allow_activating = true;
598 #undef DEBUG_ACCELERATOR_HANDLING
599 #ifdef DEBUG_ACCELERATOR_HANDLING
600 //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
601 bool debug=true;
602 #endif
603 if (focus) {
604 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
605 special_handling_of_unmodified_accelerators = true;
609 #ifdef GTKOSX
610 /* should this be universally true? */
611 if (Keyboard::some_magic_widget_has_focus ()) {
612 allow_activating = false;
614 #endif
616 #ifdef DEBUG_ACCELERATOR_HANDLING
617 if (debug) {
618 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
619 << special_handling_of_unmodified_accelerators
620 << " magic widget focus ? "
621 << Keyboard::some_magic_widget_has_focus()
622 << " allow_activation ? "
623 << allow_activating
624 << endl;
626 #endif
628 /* This exists to allow us to override the way GTK handles
629 key events. The normal sequence is:
631 a) event is delivered to a GtkWindow
632 b) accelerators/mnemonics are activated
633 c) if (b) didn't handle the event, propagate to
634 the focus widget and/or focus chain
636 The problem with this is that if the accelerators include
637 keys without modifiers, such as the space bar or the
638 letter "e", then pressing the key while typing into
639 a text entry widget results in the accelerator being
640 activated, instead of the desired letter appearing
641 in the text entry.
643 There is no good way of fixing this, but this
644 represents a compromise. The idea is that
645 key events involving modifiers (not Shift)
646 get routed into the activation pathway first, then
647 get propagated to the focus widget if necessary.
649 If the key event doesn't involve modifiers,
650 we deliver to the focus widget first, thus allowing
651 it to get "normal text" without interference
652 from acceleration.
654 Of course, this can also be problematic: if there
655 is a widget with focus, then it will swallow
656 all "normal text" accelerators.
659 #ifdef GTKOSX
660 if (!special_handling_of_unmodified_accelerators) {
661 if (ev->state & GDK_MOD1_MASK) {
662 /* we're not in a text entry or "magic focus" widget so we don't want OS X "special-character"
663 text-style handling of alt-<key>. change the keyval back to what it would be without
664 the alt key. this way, we see <alt>-v rather than <alt>-radical and so on.
666 guint keyval_without_alt = osx_keyval_without_alt (ev->keyval);
668 if (keyval_without_alt != GDK_VoidSymbol) {
669 #ifdef DEBUG_ACCELERATOR_HANDLING
670 cerr << "Remapped " << gdk_keyval_name (ev->keyval) << " to " << gdk_keyval_name (keyval_without_alt) << endl;
672 #endif ev->keyval = keyval_without_alt;
676 #endif
678 if (!special_handling_of_unmodified_accelerators) {
680 /* pretend that certain key events that GTK does not allow
681 to be used as accelerators are actually something that
682 it does allow.
685 uint32_t fakekey = ev->keyval;
687 if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
688 if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
689 return true;
694 /* consider all relevant modifiers but not LOCK or SHIFT */
696 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
698 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
700 /* no special handling or there are modifiers in effect: accelerate first */
702 #ifdef DEBUG_ACCELERATOR_HANDLING
703 if (debug) {
704 cerr << "\tactivate, then propagate\n";
706 #endif
708 if (allow_activating) {
709 if (gtk_window_activate_key (win, ev)) {
710 return true;
714 #ifdef DEBUG_ACCELERATOR_HANDLING
715 if (debug) {
716 cerr << "\tnot accelerated, now propagate\n";
718 #endif
719 return gtk_window_propagate_key_event (win, ev);
722 /* no modifiers, propagate first */
724 #ifdef DEBUG_ACCELERATOR_HANDLING
725 if (debug) {
726 cerr << "\tpropagate, then activate\n";
728 #endif
729 if (!gtk_window_propagate_key_event (win, ev)) {
730 #ifdef DEBUG_ACCELERATOR_HANDLING
731 if (debug) {
732 cerr << "\tpropagation didn't handle, so activate\n";
734 #endif
736 if (allow_activating) {
737 return gtk_window_activate_key (win, ev);
740 } else {
741 #ifdef DEBUG_ACCELERATOR_HANDLING
742 if (debug) {
743 cerr << "\thandled by propagate\n";
745 #endif
746 return true;
749 #ifdef DEBUG_ACCELERATOR_HANDLING
750 if (debug) {
751 cerr << "\tnot handled\n";
753 #endif
754 return true;
757 Glib::RefPtr<Gdk::Pixbuf>
758 get_xpm (std::string name)
760 if (!xpm_map[name]) {
762 SearchPath spath(ARDOUR::ardour_search_path());
763 spath += ARDOUR::system_data_search_path();
765 spath.add_subdirectory_to_paths("pixmaps");
767 sys::path data_file_path;
769 if(!find_file_in_search_path (spath, name, data_file_path)) {
770 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
773 try {
774 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
775 } catch(const Glib::Error& e) {
776 warning << "Caught Glib::Error: " << e.what() << endmsg;
780 return xpm_map[name];
783 Glib::ustring
784 get_icon_path (const char* cname)
786 string name = cname;
787 name += X_(".png");
789 SearchPath spath(ARDOUR::ardour_search_path());
790 spath += ARDOUR::system_data_search_path();
792 spath.add_subdirectory_to_paths("icons");
794 sys::path data_file_path;
796 if (!find_file_in_search_path (spath, name, data_file_path)) {
797 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
800 return data_file_path.to_string();
803 Glib::RefPtr<Gdk::Pixbuf>
804 get_icon (const char* cname)
806 Glib::RefPtr<Gdk::Pixbuf> img;
807 try {
808 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
809 } catch (const Gdk::PixbufError &e) {
810 cerr << "Caught PixbufError: " << e.what() << endl;
811 } catch (...) {
812 g_message("Caught ... ");
815 return img;
818 string
819 longest (vector<string>& strings)
821 if (strings.empty()) {
822 return string ("");
825 vector<string>::iterator longest = strings.begin();
826 string::size_type longest_length = (*longest).length();
828 vector<string>::iterator i = longest;
829 ++i;
831 while (i != strings.end()) {
833 string::size_type len = (*i).length();
835 if (len > longest_length) {
836 longest = i;
837 longest_length = len;
840 ++i;
843 return *longest;
846 bool
847 key_is_legal_for_numeric_entry (guint keyval)
849 switch (keyval) {
850 case GDK_minus:
851 case GDK_plus:
852 case GDK_period:
853 case GDK_comma:
854 case GDK_0:
855 case GDK_1:
856 case GDK_2:
857 case GDK_3:
858 case GDK_4:
859 case GDK_5:
860 case GDK_6:
861 case GDK_7:
862 case GDK_8:
863 case GDK_9:
864 case GDK_KP_Add:
865 case GDK_KP_Subtract:
866 case GDK_KP_Decimal:
867 case GDK_KP_0:
868 case GDK_KP_1:
869 case GDK_KP_2:
870 case GDK_KP_3:
871 case GDK_KP_4:
872 case GDK_KP_5:
873 case GDK_KP_6:
874 case GDK_KP_7:
875 case GDK_KP_8:
876 case GDK_KP_9:
877 case GDK_Return:
878 case GDK_BackSpace:
879 case GDK_Delete:
880 case GDK_KP_Enter:
881 case GDK_Home:
882 case GDK_End:
883 case GDK_Left:
884 case GDK_Right:
885 return true;
887 default:
888 break;
891 return false;
893 void
894 set_pango_fontsize ()
896 long val = ARDOUR::Config->get_font_scale();
898 /* FT2 rendering - used by GnomeCanvas, sigh */
900 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
902 /* Cairo rendering, in case there is any */
904 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
907 void
908 reset_dpi ()
910 long val = ARDOUR::Config->get_font_scale();
911 set_pango_fontsize ();
912 /* Xft rendering */
914 gtk_settings_set_long_property (gtk_settings_get_default(),
915 "gtk-xft-dpi", val, "ardour");
916 DPIReset();//Emit Signal
921 inline guint8
922 convert_color_channel (guint8 src,
923 guint8 alpha)
925 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
928 void
929 convert_bgra_to_rgba (guint8 const* src,
930 guint8* dst,
931 int width,
932 int height)
934 guint8 const* src_pixel = src;
935 guint8* dst_pixel = dst;
937 for (int y = 0; y < height; y++)
938 for (int x = 0; x < width; x++)
940 dst_pixel[0] = convert_color_channel (src_pixel[2],
941 src_pixel[3]);
942 dst_pixel[1] = convert_color_channel (src_pixel[1],
943 src_pixel[3]);
944 dst_pixel[2] = convert_color_channel (src_pixel[0],
945 src_pixel[3]);
946 dst_pixel[3] = src_pixel[3];
948 dst_pixel += 4;
949 src_pixel += 4;
953 void
954 resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
956 Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
957 Gdk::Rectangle monitor_rect;
958 screen->get_monitor_geometry (0, monitor_rect);
960 int const w = std::min (monitor_rect.get_width(), max_width) * 0.8;
961 int const h = std::min (monitor_rect.get_height(), max_height) * 0.8;
963 window->resize (w, h);
966 Glib::RefPtr<Gdk::Pixbuf>
967 pixbuf_from_ustring(const ustring& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg)
969 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
971 if (name.empty()) {
972 if (empty_pixbuf == 0) {
973 empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
974 *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
976 return *empty_pixbuf;
979 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
981 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
982 cairo_t* cr = cairo_create (surface);
983 cairo_text_extents_t te;
985 cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
986 cairo_select_font_face (cr, font->get_family().c_str(),
987 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
988 cairo_set_font_size (cr, font->get_size() / Pango::SCALE);
989 cairo_text_extents (cr, name.c_str(), &te);
991 cairo_move_to (cr, 0.5, 0.5 - te.height / 2 - te.y_bearing + clip_height / 2);
992 cairo_show_text (cr, name.c_str());
994 convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
996 cairo_destroy(cr);
997 cairo_surface_destroy(surface);
999 return buf;
1002 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
1003 string
1004 escape_underscores (string const & s)
1006 string o;
1007 string::size_type const N = s.length ();
1009 for (string::size_type i = 0; i < N; ++i) {
1010 if (s[i] == '_') {
1011 o += "__";
1012 } else {
1013 o += s[i];
1017 return o;
1020 static void
1021 adjustment_to_controllable (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
1023 boost::shared_ptr<Controllable> cont = wcont.lock();
1025 if (cont) {
1026 double val = adj->get_value();
1027 if (val != cont->get_value()) {
1028 cont->set_value (val);
1033 static void
1034 controllable_to_adjustment (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
1036 boost::shared_ptr<Controllable> cont = wcont.lock();
1038 if (cont) {
1039 float val = cont->get_value();
1041 if (val != adj->get_value()) {
1042 adj->set_value (val);
1047 void
1048 control_link (ScopedConnectionList& scl, boost::shared_ptr<Controllable> c, Gtk::Adjustment& a)
1050 boost::weak_ptr<Controllable> wc (c);
1052 a.signal_value_changed().connect (sigc::bind (sigc::ptr_fun (adjustment_to_controllable), &a, wc));
1053 c->Changed.connect (scl, MISSING_INVALIDATOR, boost::bind (controllable_to_adjustment, &a, wc),
1054 gui_context());