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
27 #include <libart_lgpl/art_misc.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/configuration.h"
41 #include "ardour/filesystem_paths.h"
43 #include "ardour_ui.h"
47 #include "rgb_macros.h"
48 #include "canvas_impl.h"
56 sigc::signal<void> DPIReset;
59 pixel_width (const ustring& str, Pango::FontDescription& font)
62 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
64 layout->set_font_description (font);
65 layout->set_text (str);
68 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
73 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
76 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
77 ustring::size_type shorter_by = 0;
80 layout->set_font_description (font);
85 ustring::iterator last = ustr.end();
86 --last; /* now points at final entry */
90 while (!ustr.empty()) {
92 layout->set_text (txt);
95 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
97 if (width < pixel_width) {
105 if (with_ellipses && shorter_by > 3) {
117 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
123 /* xpm2rgb copied from nixieclock, which bore the legend:
125 nixieclock - a nixie desktop timepiece
126 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
128 and was released under the GPL.
132 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
134 static long vals[256], val;
135 uint32_t t, x, y, colors, cpp;
137 unsigned char *savergb, *rgb;
141 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
142 error << string_compose (_("bad XPM header %1"), xpm[0])
147 savergb = rgb = (unsigned char*) malloc (h * w * 3);
149 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
150 for (t = 0; t < colors; ++t) {
151 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
155 // COLORMAP -> RGB CONVERSION
156 // Get low 3 bytes from vals[]
160 for (y = h-1; y > 0; --y) {
162 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
163 val = vals[(int)*p++];
164 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
165 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
166 *(rgb+0) = val & 0xff; // 0:R
174 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
176 static long vals[256], val;
177 uint32_t t, x, y, colors, cpp;
179 unsigned char *savergb, *rgb;
184 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
185 error << string_compose (_("bad XPM header %1"), xpm[0])
190 savergb = rgb = (unsigned char*) malloc (h * w * 4);
192 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
194 if (strstr (xpm[1], "None")) {
195 sscanf (xpm[1], "%c", &transparent);
202 for (; t < colors; ++t) {
203 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
207 // COLORMAP -> RGB CONVERSION
208 // Get low 3 bytes from vals[]
212 for (y = h-1; y > 0; --y) {
216 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
218 if (transparent && (*p++ == transparent)) {
226 *(rgb+3) = alpha; // 3: alpha
227 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
228 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
229 *(rgb+0) = val & 0xff; // 0:R
236 ArdourCanvas::Points*
237 get_canvas_points (string who, uint32_t npoints)
239 // cerr << who << ": wants " << npoints << " canvas points" << endl;
240 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
241 if (npoints > (uint32_t) gdk_screen_width() + 4) {
245 return new ArdourCanvas::Points (npoints);
248 Pango::FontDescription*
249 get_font_for_style (string widgetname)
251 Gtk::Window window (WINDOW_TOPLEVEL);
253 Glib::RefPtr<Gtk::Style> style;
256 foobar.set_name (widgetname);
257 foobar.ensure_style();
259 style = foobar.get_style ();
261 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
263 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
267 /* layout inherited its font description from a PangoContext */
269 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
270 pfd = pango_context_get_font_description (ctxt);
271 return new Pango::FontDescription (pfd, true); /* make a copy */
274 return new Pango::FontDescription (pfd, true); /* make a copy */
278 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
280 /* In GTK+2, styles aren't set up correctly if the widget is not
281 attached to a toplevel window that has a screen pointer.
284 static Gtk::Window* window = 0;
287 window = new Window (WINDOW_TOPLEVEL);
294 foo.set_name (style);
297 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
301 r = waverc->fg[state].red / 257;
302 g = waverc->fg[state].green / 257;
303 b = waverc->fg[state].blue / 257;
305 /* what a hack ... "a" is for "active" */
306 if (state == Gtk::STATE_NORMAL && rgba) {
307 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
309 } else if (attr == "bg") {
311 r = waverc->bg[state].red / 257;
312 g = waverc->bg[state].green / 257;
313 b = waverc->bg[state].blue / 257;
314 } else if (attr == "base") {
315 r = waverc->base[state].red / 257;
316 g = waverc->base[state].green / 257;
317 b = waverc->base[state].blue / 257;
318 } else if (attr == "text") {
319 r = waverc->text[state].red / 257;
320 g = waverc->text[state].green / 257;
321 b = waverc->text[state].blue / 257;
324 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
329 if (state == Gtk::STATE_NORMAL && rgba) {
330 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
332 return (uint32_t) RGB_TO_UINT(r,g,b);
338 color_from_style (string widget_style_name, int state, string attr)
342 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
343 widget_style_name.c_str(),
347 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
348 return Gdk::Color ("red");
352 return Gdk::Color (&style->fg[state]);
356 return Gdk::Color (&style->bg[state]);
359 if (attr == "light") {
360 return Gdk::Color (&style->light[state]);
363 if (attr == "dark") {
364 return Gdk::Color (&style->dark[state]);
368 return Gdk::Color (&style->mid[state]);
371 if (attr == "text") {
372 return Gdk::Color (&style->text[state]);
375 if (attr == "base") {
376 return Gdk::Color (&style->base[state]);
379 if (attr == "text_aa") {
380 return Gdk::Color (&style->text_aa[state]);
383 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
384 return Gdk::Color ("red");
387 Glib::RefPtr<Gdk::GC>
388 gc_from_style (string widget_style_name, int state, string attr)
392 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
393 widget_style_name.c_str(),
397 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
398 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
399 ret->set_rgb_fg_color(Gdk::Color("red"));
404 return Glib::wrap(style->fg_gc[state]);
408 return Glib::wrap(style->bg_gc[state]);
411 if (attr == "light") {
412 return Glib::wrap(style->light_gc[state]);
415 if (attr == "dark") {
416 return Glib::wrap(style->dark_gc[state]);
420 return Glib::wrap(style->mid_gc[state]);
423 if (attr == "text") {
424 return Glib::wrap(style->text_gc[state]);
427 if (attr == "base") {
428 return Glib::wrap(style->base_gc[state]);
431 if (attr == "text_aa") {
432 return Glib::wrap(style->text_aa_gc[state]);
435 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
436 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
437 ret->set_rgb_fg_color(Gdk::Color("red"));
443 canvas_item_visible (ArdourCanvas::Item* item)
445 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
449 set_color (Gdk::Color& c, int rgb)
451 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
456 gboolean gdk_quartz_possibly_forward (GdkEvent*);
461 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
463 GtkWindow* win = window.gobj();
464 GtkWidget* focus = gtk_window_get_focus (win);
465 bool special_handling_of_unmodified_accelerators = false;
466 bool allow_activating = true;
468 #undef DEBUG_ACCELERATOR_HANDLING
469 #ifdef DEBUG_ACCELERATOR_HANDLING
470 //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
474 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
475 special_handling_of_unmodified_accelerators = true;
480 /* should this be universally true? */
481 if (Keyboard::some_magic_widget_has_focus ()) {
482 allow_activating = false;
486 #ifdef DEBUG_ACCELERATOR_HANDLING
488 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
489 << special_handling_of_unmodified_accelerators
490 << " magic widget focus ? "
491 << Keyboard::some_magic_widget_has_focus()
492 << " allow_activation ? "
498 /* This exists to allow us to override the way GTK handles
499 key events. The normal sequence is:
501 a) event is delivered to a GtkWindow
502 b) accelerators/mnemonics are activated
503 c) if (b) didn't handle the event, propagate to
504 the focus widget and/or focus chain
506 The problem with this is that if the accelerators include
507 keys without modifiers, such as the space bar or the
508 letter "e", then pressing the key while typing into
509 a text entry widget results in the accelerator being
510 activated, instead of the desired letter appearing
513 There is no good way of fixing this, but this
514 represents a compromise. The idea is that
515 key events involving modifiers (not Shift)
516 get routed into the activation pathway first, then
517 get propagated to the focus widget if necessary.
519 If the key event doesn't involve modifiers,
520 we deliver to the focus widget first, thus allowing
521 it to get "normal text" without interference
524 Of course, this can also be problematic: if there
525 is a widget with focus, then it will swallow
526 all "normal text" accelerators.
530 if (!special_handling_of_unmodified_accelerators) {
532 /* pretend that certain key events that GTK does not allow
533 to be used as accelerators are actually something that
537 uint32_t fakekey = ev->keyval;
539 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
540 if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
545 if (allow_activating) {
546 int oldval = ev->keyval;
547 ev->keyval = fakekey;
548 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
557 /* consider all relevant modifiers but not LOCK or SHIFT */
559 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
561 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
563 /* no special handling or there are modifiers in effect: accelerate first */
565 #ifdef DEBUG_ACCELERATOR_HANDLING
567 cerr << "\tactivate, then propagate\n";
571 if (allow_activating) {
573 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
577 if (gtk_window_activate_key (win, ev)) {
582 #ifdef DEBUG_ACCELERATOR_HANDLING
584 cerr << "\tnot accelerated, now propagate\n";
587 return gtk_window_propagate_key_event (win, ev);
590 /* no modifiers, propagate first */
592 #ifdef DEBUG_ACCELERATOR_HANDLING
594 cerr << "\tpropagate, then activate\n";
597 if (!gtk_window_propagate_key_event (win, ev)) {
598 #ifdef DEBUG_ACCELERATOR_HANDLING
600 cerr << "\tpropagation didn't handle, so activate\n";
604 if (allow_activating) {
607 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
611 return gtk_window_activate_key (win, ev);
615 #ifdef DEBUG_ACCELERATOR_HANDLING
617 cerr << "\thandled by propagate\n";
623 #ifdef DEBUG_ACCELERATOR_HANDLING
625 cerr << "\tnot handled\n";
631 Glib::RefPtr<Gdk::Pixbuf>
632 get_xpm (std::string name)
634 if (!xpm_map[name]) {
636 SearchPath spath(ARDOUR::ardour_search_path());
637 spath += ARDOUR::system_data_search_path();
639 spath.add_subdirectory_to_paths("pixmaps");
641 sys::path data_file_path;
643 if(!find_file_in_search_path (spath, name, data_file_path)) {
644 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
648 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
649 } catch(const Glib::Error& e) {
650 warning << "Caught Glib::Error: " << e.what() << endmsg;
654 return xpm_map[name];
658 Glib::RefPtr<Gdk::Pixbuf>
659 get_icon (const char* cname)
664 SearchPath spath(ARDOUR::ardour_search_path());
665 spath += ARDOUR::system_data_search_path();
667 spath.add_subdirectory_to_paths("icons");
669 sys::path data_file_path;
671 if (!find_file_in_search_path (spath, name, data_file_path)) {
672 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
675 Glib::RefPtr<Gdk::Pixbuf> img;
677 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
678 } catch (const Gdk::PixbufError &e) {
679 cerr << "Caught PixbufError: " << e.what() << endl;
681 g_message("Caught ... ");
688 longest (vector<string>& strings)
690 if (strings.empty()) {
694 vector<string>::iterator longest = strings.begin();
695 string::size_type longest_length = (*longest).length();
697 vector<string>::iterator i = longest;
700 while (i != strings.end()) {
702 string::size_type len = (*i).length();
704 if (len > longest_length) {
706 longest_length = len;
716 key_is_legal_for_numeric_entry (guint keyval)
734 case GDK_KP_Subtract:
763 set_pango_fontsize ()
765 long val = ARDOUR::Config->get_font_scale();
767 /* FT2 rendering - used by GnomeCanvas, sigh */
769 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
771 /* Cairo rendering, in case there is any */
773 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
779 long val = ARDOUR::Config->get_font_scale();
780 set_pango_fontsize ();
783 gtk_settings_set_long_property (gtk_settings_get_default(),
784 "gtk-xft-dpi", val, "ardour");
785 DPIReset();//Emit Signal
789 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
791 int fakekey = GDK_VoidSymbol;
795 case GDK_ISO_Left_Tab:
800 fakekey = GDK_uparrow;
804 fakekey = GDK_downarrow;
808 fakekey = GDK_rightarrow;
812 fakekey = GDK_leftarrow;
819 if (fakekey != GDK_VoidSymbol) {
829 convert_color_channel (guint8 src,
832 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
836 convert_bgra_to_rgba (guint8 const* src,
841 guint8 const* src_pixel = src;
842 guint8* dst_pixel = dst;
844 for (int y = 0; y < height; y++)
845 for (int x = 0; x < width; x++)
847 dst_pixel[0] = convert_color_channel (src_pixel[2],
849 dst_pixel[1] = convert_color_channel (src_pixel[1],
851 dst_pixel[2] = convert_color_channel (src_pixel[0],
853 dst_pixel[3] = src_pixel[3];