plugin manager: filter-by-type really is filter-by-category, so fix the naming and...
[ardour2.git] / gtk2_ardour / utils.cc
blob2e98fad06e7dde84e813920b3b11628d109804ee
1 /*
2 Copyright (C) 2003 Paul Davis
4 This program is free software; you can 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 <gtkmm2ext/utils.h>
36 #include <ardour/ardour.h>
37 #include <ardour/configuration.h>
39 #include "ardour_ui.h"
40 #include "keyboard.h"
41 #include "utils.h"
42 #include "i18n.h"
43 #include "rgb_macros.h"
44 #include "canvas_impl.h"
46 using namespace std;
47 using namespace Gtk;
48 using namespace sigc;
49 using namespace Glib;
50 using namespace PBD;
52 sigc::signal<void> DPIReset;
54 int
55 pixel_width (const ustring& str, Pango::FontDescription& font)
57 Label foo;
58 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
60 layout->set_font_description (font);
61 layout->set_text (str);
63 int width, height;
64 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
65 return width;
68 ustring
69 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
71 Label foo;
72 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
73 ustring::size_type shorter_by = 0;
74 ustring txt;
76 layout->set_font_description (font);
78 actual_width = 0;
80 ustring ustr = str;
81 ustring::iterator last = ustr.end();
82 --last; /* now points at final entry */
84 txt = ustr;
86 while (!ustr.empty()) {
88 layout->set_text (txt);
90 int width, height;
91 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
93 if (width < pixel_width) {
94 actual_width = width;
95 break;
98 ustr.erase (last--);
99 shorter_by++;
101 if (with_ellipses && shorter_by > 3) {
102 txt = ustr;
103 txt += "...";
104 } else {
105 txt = ustr;
109 return txt;
112 gint
113 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
115 win->hide ();
116 return TRUE;
119 /* xpm2rgb copied from nixieclock, which bore the legend:
121 nixieclock - a nixie desktop timepiece
122 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
124 and was released under the GPL.
127 unsigned char*
128 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
130 static long vals[256], val;
131 uint32_t t, x, y, colors, cpp;
132 unsigned char c;
133 unsigned char *savergb, *rgb;
135 // PARSE HEADER
137 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
138 error << string_compose (_("bad XPM header %1"), xpm[0])
139 << endmsg;
140 return 0;
143 savergb = rgb = (unsigned char*) malloc (h * w * 3);
145 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
146 for (t = 0; t < colors; ++t) {
147 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
148 vals[c] = val;
151 // COLORMAP -> RGB CONVERSION
152 // Get low 3 bytes from vals[]
155 const char *p;
156 for (y = h-1; y > 0; --y) {
158 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
159 val = vals[(int)*p++];
160 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
161 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
162 *(rgb+0) = val & 0xff; // 0:R
166 return (savergb);
169 unsigned char*
170 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
172 static long vals[256], val;
173 uint32_t t, x, y, colors, cpp;
174 unsigned char c;
175 unsigned char *savergb, *rgb;
176 char transparent;
178 // PARSE HEADER
180 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
181 error << string_compose (_("bad XPM header %1"), xpm[0])
182 << endmsg;
183 return 0;
186 savergb = rgb = (unsigned char*) malloc (h * w * 4);
188 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
190 if (strstr (xpm[1], "None")) {
191 sscanf (xpm[1], "%c", &transparent);
192 t = 1;
193 } else {
194 transparent = 0;
195 t = 0;
198 for (; t < colors; ++t) {
199 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
200 vals[c] = val;
203 // COLORMAP -> RGB CONVERSION
204 // Get low 3 bytes from vals[]
207 const char *p;
208 for (y = h-1; y > 0; --y) {
210 char alpha;
212 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
214 if (transparent && (*p++ == transparent)) {
215 alpha = 0;
216 val = 0;
217 } else {
218 alpha = 255;
219 val = vals[(int)*p];
222 *(rgb+3) = alpha; // 3: alpha
223 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
224 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
225 *(rgb+0) = val & 0xff; // 0:R
229 return (savergb);
232 ArdourCanvas::Points*
233 get_canvas_points (string who, uint32_t npoints)
235 // cerr << who << ": wants " << npoints << " canvas points" << endl;
236 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
237 if (npoints > (uint32_t) gdk_screen_width() + 4) {
238 abort ();
240 #endif
241 return new ArdourCanvas::Points (npoints);
244 Pango::FontDescription*
245 get_font_for_style (string widgetname)
247 Gtk::Window window (WINDOW_TOPLEVEL);
248 Gtk::Label foobar;
249 Glib::RefPtr<Gtk::Style> style;
251 window.add (foobar);
252 foobar.set_name (widgetname);
253 foobar.ensure_style();
255 style = foobar.get_style ();
257 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
259 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
261 if (!pfd) {
263 /* layout inherited its font description from a PangoContext */
265 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
266 pfd = pango_context_get_font_description (ctxt);
267 return new Pango::FontDescription (pfd, true); /* make a copy */
270 return new Pango::FontDescription (pfd, true); /* make a copy */
273 uint32_t
274 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
276 /* In GTK+2, styles aren't set up correctly if the widget is not
277 attached to a toplevel window that has a screen pointer.
280 static Gtk::Window* window = 0;
282 if (window == 0) {
283 window = new Window (WINDOW_TOPLEVEL);
286 Gtk::Label foo;
288 window->add (foo);
290 foo.set_name (style);
291 foo.ensure_style ();
293 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
295 if (waverc) {
296 if (attr == "fg") {
297 r = waverc->fg[state].red / 257;
298 g = waverc->fg[state].green / 257;
299 b = waverc->fg[state].blue / 257;
301 /* what a hack ... "a" is for "active" */
302 if (state == Gtk::STATE_NORMAL && rgba) {
303 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
305 } else if (attr == "bg") {
306 r = g = b = 0;
307 r = waverc->bg[state].red / 257;
308 g = waverc->bg[state].green / 257;
309 b = waverc->bg[state].blue / 257;
310 } else if (attr == "base") {
311 r = waverc->base[state].red / 257;
312 g = waverc->base[state].green / 257;
313 b = waverc->base[state].blue / 257;
314 } else if (attr == "text") {
315 r = waverc->text[state].red / 257;
316 g = waverc->text[state].green / 257;
317 b = waverc->text[state].blue / 257;
319 } else {
320 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
323 window->remove ();
325 if (state == Gtk::STATE_NORMAL && rgba) {
326 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
327 } else {
328 return (uint32_t) RGB_TO_UINT(r,g,b);
333 Gdk::Color
334 color_from_style (string widget_style_name, int state, string attr)
336 GtkStyle* style;
338 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
339 widget_style_name.c_str(),
340 0, G_TYPE_NONE);
342 if (!style) {
343 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
344 return Gdk::Color ("red");
347 cerr << "got style for " << widget_style_name << endl;
349 if (attr == "fg") {
350 return Gdk::Color (&style->fg[state]);
353 if (attr == "bg") {
354 cerr << "returning color from bg\n";
355 return Gdk::Color (&style->bg[state]);
358 if (attr == "light") {
359 return Gdk::Color (&style->light[state]);
362 if (attr == "dark") {
363 return Gdk::Color (&style->dark[state]);
366 if (attr == "mid") {
367 return Gdk::Color (&style->mid[state]);
370 if (attr == "text") {
371 return Gdk::Color (&style->text[state]);
374 if (attr == "base") {
375 return Gdk::Color (&style->base[state]);
378 if (attr == "text_aa") {
379 return Gdk::Color (&style->text_aa[state]);
382 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
383 return Gdk::Color ("red");
387 bool
388 canvas_item_visible (ArdourCanvas::Item* item)
390 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
393 void
394 set_color (Gdk::Color& c, int rgb)
396 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
399 #ifdef GTKOSX
400 extern "C" {
401 gboolean gdk_quartz_possibly_forward (GdkEvent*);
403 #endif
405 bool
406 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
408 GtkWindow* win = window.gobj();
409 GtkWidget* focus = gtk_window_get_focus (win);
410 bool special_handling_of_unmodified_accelerators = false;
411 bool allow_activating = true;
413 #undef DEBUG_ACCELERATOR_HANDLING
414 #ifdef DEBUG_ACCELERATOR_HANDLING
415 //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
416 bool debug=true;
417 #endif
418 if (focus) {
419 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
420 special_handling_of_unmodified_accelerators = true;
424 #ifdef GTKOSX
425 /* should this be universally true? */
426 if (Keyboard::some_magic_widget_has_focus ()) {
427 allow_activating = false;
429 #endif
431 #ifdef DEBUG_ACCELERATOR_HANDLING
432 if (debug) {
433 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
434 << special_handling_of_unmodified_accelerators
435 << " magic widget focus ? "
436 << Keyboard::some_magic_widget_has_focus()
437 << " allow_activation ? "
438 << allow_activating
439 << endl;
441 #endif
443 /* This exists to allow us to override the way GTK handles
444 key events. The normal sequence is:
446 a) event is delivered to a GtkWindow
447 b) accelerators/mnemonics are activated
448 c) if (b) didn't handle the event, propagate to
449 the focus widget and/or focus chain
451 The problem with this is that if the accelerators include
452 keys without modifiers, such as the space bar or the
453 letter "e", then pressing the key while typing into
454 a text entry widget results in the accelerator being
455 activated, instead of the desired letter appearing
456 in the text entry.
458 There is no good way of fixing this, but this
459 represents a compromise. The idea is that
460 key events involving modifiers (not Shift)
461 get routed into the activation pathway first, then
462 get propagated to the focus widget if necessary.
464 If the key event doesn't involve modifiers,
465 we deliver to the focus widget first, thus allowing
466 it to get "normal text" without interference
467 from acceleration.
469 Of course, this can also be problematic: if there
470 is a widget with focus, then it will swallow
471 all "normal text" accelerators.
475 if (!special_handling_of_unmodified_accelerators) {
477 /* pretend that certain key events that GTK does not allow
478 to be used as accelerators are actually something that
479 it does allow.
482 uint32_t fakekey = ev->keyval;
484 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
485 if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
486 return true;
489 #ifdef GTKOSX
490 if (allow_activating) {
491 int oldval = ev->keyval;
492 ev->keyval = fakekey;
493 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
494 return true;
496 ev->keyval = oldval;
498 #endif
502 /* consider all relevant modifiers but not LOCK or SHIFT */
504 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
506 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
508 /* no special handling or there are modifiers in effect: accelerate first */
510 #ifdef DEBUG_ACCELERATOR_HANDLING
511 if (debug) {
512 cerr << "\tactivate, then propagate\n";
514 #endif
516 if (allow_activating) {
517 #ifdef GTKOSX
518 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
519 return true;
521 #endif
522 if (gtk_window_activate_key (win, ev)) {
523 return true;
527 #ifdef DEBUG_ACCELERATOR_HANDLING
528 if (debug) {
529 cerr << "\tnot accelerated, now propagate\n";
531 #endif
532 return gtk_window_propagate_key_event (win, ev);
535 /* no modifiers, propagate first */
537 #ifdef DEBUG_ACCELERATOR_HANDLING
538 if (debug) {
539 cerr << "\tpropagate, then activate\n";
541 #endif
542 if (!gtk_window_propagate_key_event (win, ev)) {
543 #ifdef DEBUG_ACCELERATOR_HANDLING
544 if (debug) {
545 cerr << "\tpropagation didn't handle, so activate\n";
547 #endif
549 if (allow_activating) {
551 #ifdef GTKOSX
552 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
553 return true;
555 #endif
556 return gtk_window_activate_key (win, ev);
559 } else {
560 #ifdef DEBUG_ACCELERATOR_HANDLING
561 if (debug) {
562 cerr << "\thandled by propagate\n";
564 #endif
565 return true;
568 #ifdef DEBUG_ACCELERATOR_HANDLING
569 if (debug) {
570 cerr << "\tnot handled\n";
572 #endif
573 return true;
576 Glib::RefPtr<Gdk::Pixbuf>
577 get_xpm (std::string name)
579 //cerr << "xpm path = " << ARDOUR::find_data_file(name, "pixmaps") << endl;//DEBUG
580 if (!xpm_map[name]) {
581 try {
582 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
584 catch(const Glib::Error& e) {
585 warning << "Caught Glib::Error: " << e.what() << endmsg;
589 return (xpm_map[name]);
592 Glib::RefPtr<Gdk::Pixbuf>
593 get_icon (const char* cname)
595 string name = cname;
596 name += X_(".png");
598 string path = ARDOUR::find_data_file (name, "icons");
600 if (path.empty()) {
601 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
602 /*NOTREACHED*/
605 Glib::RefPtr<Gdk::Pixbuf> img;
606 try {
607 img = Gdk::Pixbuf::create_from_file (path);
609 catch (const Gdk::PixbufError &e)
611 cerr << "Caught PixbufError: " << e.what() << endl;
613 catch (...)
615 g_message("Caught ... ");
618 return img;
621 string
622 longest (vector<string>& strings)
624 if (strings.empty()) {
625 return string ("");
628 vector<string>::iterator longest = strings.begin();
629 string::size_type longest_length = (*longest).length();
631 vector<string>::iterator i = longest;
632 ++i;
634 while (i != strings.end()) {
636 string::size_type len = (*i).length();
638 if (len > longest_length) {
639 longest = i;
640 longest_length = len;
643 ++i;
646 return *longest;
649 bool
650 key_is_legal_for_numeric_entry (guint keyval)
652 switch (keyval) {
653 case GDK_minus:
654 case GDK_plus:
655 case GDK_period:
656 case GDK_comma:
657 case GDK_0:
658 case GDK_1:
659 case GDK_2:
660 case GDK_3:
661 case GDK_4:
662 case GDK_5:
663 case GDK_6:
664 case GDK_7:
665 case GDK_8:
666 case GDK_9:
667 case GDK_KP_Add:
668 case GDK_KP_Subtract:
669 case GDK_KP_Decimal:
670 case GDK_KP_0:
671 case GDK_KP_1:
672 case GDK_KP_2:
673 case GDK_KP_3:
674 case GDK_KP_4:
675 case GDK_KP_5:
676 case GDK_KP_6:
677 case GDK_KP_7:
678 case GDK_KP_8:
679 case GDK_KP_9:
680 case GDK_Return:
681 case GDK_BackSpace:
682 case GDK_Delete:
683 case GDK_KP_Enter:
684 case GDK_Home:
685 case GDK_End:
686 case GDK_Left:
687 case GDK_Right:
688 return true;
690 default:
691 break;
694 return false;
696 void
697 set_pango_fontsize ()
699 long val = ARDOUR::Config->get_font_scale();
701 /* FT2 rendering - used by GnomeCanvas, sigh */
703 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
705 /* Cairo rendering, in case there is any */
707 // pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
710 void
711 reset_dpi ()
713 long val = ARDOUR::Config->get_font_scale();
714 set_pango_fontsize ();
715 /* Xft rendering */
717 gtk_settings_set_long_property (gtk_settings_get_default(),
718 "gtk-xft-dpi", val, "ardour");
719 DPIReset();//Emit Signal
722 bool
723 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
725 int fakekey = GDK_VoidSymbol;
727 switch (keyval) {
728 case GDK_Tab:
729 case GDK_ISO_Left_Tab:
730 fakekey = GDK_nabla;
731 break;
733 case GDK_Up:
734 fakekey = GDK_uparrow;
735 break;
737 case GDK_Down:
738 fakekey = GDK_downarrow;
739 break;
741 case GDK_Right:
742 fakekey = GDK_rightarrow;
743 break;
745 case GDK_Left:
746 fakekey = GDK_leftarrow;
747 break;
749 default:
750 break;
753 if (fakekey != GDK_VoidSymbol) {
754 keyval = fakekey;
755 return true;
758 return false;