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) {
116 std::pair<std::string, double>
117 fit_to_pixels (cairo_t* cr, std::string name, double avail)
119 /* XXX hopefully there exists a more efficient way of doing this */
121 bool abbreviated = false;
125 cairo_text_extents_t ext;
126 cairo_text_extents (cr, name.c_str(), &ext);
128 if (ext.width < avail || name.length() <= 4) {
134 name = name.substr (0, name.length() - 4) + "...";
136 name = name.substr (0, name.length() - 3) + "...";
141 return std::make_pair (name, width);
146 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
152 /* xpm2rgb copied from nixieclock, which bore the legend:
154 nixieclock - a nixie desktop timepiece
155 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
157 and was released under the GPL.
161 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
163 static long vals[256], val;
164 uint32_t t, x, y, colors, cpp;
166 unsigned char *savergb, *rgb;
170 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
171 error << string_compose (_("bad XPM header %1"), xpm[0])
176 savergb = rgb = (unsigned char*) malloc (h * w * 3);
178 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
179 for (t = 0; t < colors; ++t) {
180 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
184 // COLORMAP -> RGB CONVERSION
185 // Get low 3 bytes from vals[]
189 for (y = h-1; y > 0; --y) {
191 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
192 val = vals[(int)*p++];
193 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
194 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
195 *(rgb+0) = val & 0xff; // 0:R
203 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
205 static long vals[256], val;
206 uint32_t t, x, y, colors, cpp;
208 unsigned char *savergb, *rgb;
213 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
214 error << string_compose (_("bad XPM header %1"), xpm[0])
219 savergb = rgb = (unsigned char*) malloc (h * w * 4);
221 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
223 if (strstr (xpm[1], "None")) {
224 sscanf (xpm[1], "%c", &transparent);
231 for (; t < colors; ++t) {
232 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
236 // COLORMAP -> RGB CONVERSION
237 // Get low 3 bytes from vals[]
241 for (y = h-1; y > 0; --y) {
245 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
247 if (transparent && (*p++ == transparent)) {
255 *(rgb+3) = alpha; // 3: alpha
256 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
257 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
258 *(rgb+0) = val & 0xff; // 0:R
265 ArdourCanvas::Points*
266 get_canvas_points (string who, uint32_t npoints)
268 // cerr << who << ": wants " << npoints << " canvas points" << endl;
269 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
270 if (npoints > (uint32_t) gdk_screen_width() + 4) {
274 return new ArdourCanvas::Points (npoints);
277 Pango::FontDescription*
278 get_font_for_style (string widgetname)
280 Gtk::Window window (WINDOW_TOPLEVEL);
282 Glib::RefPtr<Gtk::Style> style;
285 foobar.set_name (widgetname);
286 foobar.ensure_style();
288 style = foobar.get_style ();
290 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
292 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
296 /* layout inherited its font description from a PangoContext */
298 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
299 pfd = pango_context_get_font_description (ctxt);
300 return new Pango::FontDescription (pfd, true); /* make a copy */
303 return new Pango::FontDescription (pfd, true); /* make a copy */
307 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
309 /* In GTK+2, styles aren't set up correctly if the widget is not
310 attached to a toplevel window that has a screen pointer.
313 static Gtk::Window* window = 0;
316 window = new Window (WINDOW_TOPLEVEL);
323 foo.set_name (style);
326 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
330 r = waverc->fg[state].red / 257;
331 g = waverc->fg[state].green / 257;
332 b = waverc->fg[state].blue / 257;
334 /* what a hack ... "a" is for "active" */
335 if (state == Gtk::STATE_NORMAL && rgba) {
336 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
338 } else if (attr == "bg") {
340 r = waverc->bg[state].red / 257;
341 g = waverc->bg[state].green / 257;
342 b = waverc->bg[state].blue / 257;
343 } else if (attr == "base") {
344 r = waverc->base[state].red / 257;
345 g = waverc->base[state].green / 257;
346 b = waverc->base[state].blue / 257;
347 } else if (attr == "text") {
348 r = waverc->text[state].red / 257;
349 g = waverc->text[state].green / 257;
350 b = waverc->text[state].blue / 257;
353 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
358 if (state == Gtk::STATE_NORMAL && rgba) {
359 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
361 return (uint32_t) RGB_TO_UINT(r,g,b);
367 color_from_style (string widget_style_name, int state, string attr)
371 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
372 widget_style_name.c_str(),
376 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
377 return Gdk::Color ("red");
381 return Gdk::Color (&style->fg[state]);
385 return Gdk::Color (&style->bg[state]);
388 if (attr == "light") {
389 return Gdk::Color (&style->light[state]);
392 if (attr == "dark") {
393 return Gdk::Color (&style->dark[state]);
397 return Gdk::Color (&style->mid[state]);
400 if (attr == "text") {
401 return Gdk::Color (&style->text[state]);
404 if (attr == "base") {
405 return Gdk::Color (&style->base[state]);
408 if (attr == "text_aa") {
409 return Gdk::Color (&style->text_aa[state]);
412 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
413 return Gdk::Color ("red");
416 Glib::RefPtr<Gdk::GC>
417 gc_from_style (string widget_style_name, int state, string attr)
421 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
422 widget_style_name.c_str(),
426 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
427 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
428 ret->set_rgb_fg_color(Gdk::Color("red"));
433 return Glib::wrap(style->fg_gc[state]);
437 return Glib::wrap(style->bg_gc[state]);
440 if (attr == "light") {
441 return Glib::wrap(style->light_gc[state]);
444 if (attr == "dark") {
445 return Glib::wrap(style->dark_gc[state]);
449 return Glib::wrap(style->mid_gc[state]);
452 if (attr == "text") {
453 return Glib::wrap(style->text_gc[state]);
456 if (attr == "base") {
457 return Glib::wrap(style->base_gc[state]);
460 if (attr == "text_aa") {
461 return Glib::wrap(style->text_aa_gc[state]);
464 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
465 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
466 ret->set_rgb_fg_color(Gdk::Color("red"));
472 canvas_item_visible (ArdourCanvas::Item* item)
474 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
478 set_color (Gdk::Color& c, int rgb)
480 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
485 gboolean gdk_quartz_possibly_forward (GdkEvent*);
490 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
492 GtkWindow* win = window.gobj();
493 GtkWidget* focus = gtk_window_get_focus (win);
494 bool special_handling_of_unmodified_accelerators = false;
495 bool allow_activating = true;
497 #undef DEBUG_ACCELERATOR_HANDLING
498 #ifdef DEBUG_ACCELERATOR_HANDLING
499 //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
503 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
504 special_handling_of_unmodified_accelerators = true;
509 /* should this be universally true? */
510 if (Keyboard::some_magic_widget_has_focus ()) {
511 allow_activating = false;
515 #ifdef DEBUG_ACCELERATOR_HANDLING
517 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
518 << special_handling_of_unmodified_accelerators
519 << " magic widget focus ? "
520 << Keyboard::some_magic_widget_has_focus()
521 << " allow_activation ? "
527 /* This exists to allow us to override the way GTK handles
528 key events. The normal sequence is:
530 a) event is delivered to a GtkWindow
531 b) accelerators/mnemonics are activated
532 c) if (b) didn't handle the event, propagate to
533 the focus widget and/or focus chain
535 The problem with this is that if the accelerators include
536 keys without modifiers, such as the space bar or the
537 letter "e", then pressing the key while typing into
538 a text entry widget results in the accelerator being
539 activated, instead of the desired letter appearing
542 There is no good way of fixing this, but this
543 represents a compromise. The idea is that
544 key events involving modifiers (not Shift)
545 get routed into the activation pathway first, then
546 get propagated to the focus widget if necessary.
548 If the key event doesn't involve modifiers,
549 we deliver to the focus widget first, thus allowing
550 it to get "normal text" without interference
553 Of course, this can also be problematic: if there
554 is a widget with focus, then it will swallow
555 all "normal text" accelerators.
559 if (!special_handling_of_unmodified_accelerators) {
561 /* pretend that certain key events that GTK does not allow
562 to be used as accelerators are actually something that
566 uint32_t fakekey = ev->keyval;
568 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
569 if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
574 if (allow_activating) {
575 int oldval = ev->keyval;
576 ev->keyval = fakekey;
577 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
586 /* consider all relevant modifiers but not LOCK or SHIFT */
588 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
590 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
592 /* no special handling or there are modifiers in effect: accelerate first */
594 #ifdef DEBUG_ACCELERATOR_HANDLING
596 cerr << "\tactivate, then propagate\n";
600 if (allow_activating) {
602 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
606 if (gtk_window_activate_key (win, ev)) {
611 #ifdef DEBUG_ACCELERATOR_HANDLING
613 cerr << "\tnot accelerated, now propagate\n";
616 return gtk_window_propagate_key_event (win, ev);
619 /* no modifiers, propagate first */
621 #ifdef DEBUG_ACCELERATOR_HANDLING
623 cerr << "\tpropagate, then activate\n";
626 if (!gtk_window_propagate_key_event (win, ev)) {
627 #ifdef DEBUG_ACCELERATOR_HANDLING
629 cerr << "\tpropagation didn't handle, so activate\n";
633 if (allow_activating) {
636 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
640 return gtk_window_activate_key (win, ev);
644 #ifdef DEBUG_ACCELERATOR_HANDLING
646 cerr << "\thandled by propagate\n";
652 #ifdef DEBUG_ACCELERATOR_HANDLING
654 cerr << "\tnot handled\n";
660 Glib::RefPtr<Gdk::Pixbuf>
661 get_xpm (std::string name)
663 if (!xpm_map[name]) {
665 SearchPath spath(ARDOUR::ardour_search_path());
666 spath += ARDOUR::system_data_search_path();
668 spath.add_subdirectory_to_paths("pixmaps");
670 sys::path data_file_path;
672 if(!find_file_in_search_path (spath, name, data_file_path)) {
673 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
677 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
678 } catch(const Glib::Error& e) {
679 warning << "Caught Glib::Error: " << e.what() << endmsg;
683 return xpm_map[name];
687 Glib::RefPtr<Gdk::Pixbuf>
688 get_icon (const char* cname)
693 SearchPath spath(ARDOUR::ardour_search_path());
694 spath += ARDOUR::system_data_search_path();
696 spath.add_subdirectory_to_paths("icons");
698 sys::path data_file_path;
700 if (!find_file_in_search_path (spath, name, data_file_path)) {
701 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
704 Glib::RefPtr<Gdk::Pixbuf> img;
706 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
707 } catch (const Gdk::PixbufError &e) {
708 cerr << "Caught PixbufError: " << e.what() << endl;
710 g_message("Caught ... ");
717 longest (vector<string>& strings)
719 if (strings.empty()) {
723 vector<string>::iterator longest = strings.begin();
724 string::size_type longest_length = (*longest).length();
726 vector<string>::iterator i = longest;
729 while (i != strings.end()) {
731 string::size_type len = (*i).length();
733 if (len > longest_length) {
735 longest_length = len;
745 key_is_legal_for_numeric_entry (guint keyval)
763 case GDK_KP_Subtract:
792 set_pango_fontsize ()
794 long val = ARDOUR::Config->get_font_scale();
796 /* FT2 rendering - used by GnomeCanvas, sigh */
798 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
800 /* Cairo rendering, in case there is any */
802 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
808 long val = ARDOUR::Config->get_font_scale();
809 set_pango_fontsize ();
812 gtk_settings_set_long_property (gtk_settings_get_default(),
813 "gtk-xft-dpi", val, "ardour");
814 DPIReset();//Emit Signal
818 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
820 int fakekey = GDK_VoidSymbol;
824 case GDK_ISO_Left_Tab:
829 fakekey = GDK_uparrow;
833 fakekey = GDK_downarrow;
837 fakekey = GDK_rightarrow;
841 fakekey = GDK_leftarrow;
848 if (fakekey != GDK_VoidSymbol) {
858 convert_color_channel (guint8 src,
861 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
865 convert_bgra_to_rgba (guint8 const* src,
870 guint8 const* src_pixel = src;
871 guint8* dst_pixel = dst;
873 for (int y = 0; y < height; y++)
874 for (int x = 0; x < width; x++)
876 dst_pixel[0] = convert_color_channel (src_pixel[2],
878 dst_pixel[1] = convert_color_channel (src_pixel[1],
880 dst_pixel[2] = convert_color_channel (src_pixel[0],
882 dst_pixel[3] = src_pixel[3];