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 /** Try to fit a string into a given horizontal space by ellipsizing it.
117 * @param cr Cairo context in which the text will be plotted.
119 * @param avail Available horizontal space.
120 * @return (Text, possibly ellipsized) and (horizontal size of text)
123 std::pair<std::string, double>
124 fit_to_pixels (cairo_t* cr, std::string name, double avail)
126 /* XXX hopefully there exists a more efficient way of doing this */
128 bool abbreviated = false;
132 cairo_text_extents_t ext;
133 cairo_text_extents (cr, name.c_str(), &ext);
135 if (ext.width < avail || name.length() <= 4) {
141 name = name.substr (0, name.length() - 4) + "...";
143 name = name.substr (0, name.length() - 3) + "...";
148 return std::make_pair (name, width);
152 /** Add an element to a menu, settings its sensitivity.
153 * @param m Menu to add to.
154 * @param e Element to add.
155 * @param s true to make sensitive, false to make insensitive
158 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
162 m.back().set_sensitive (false);
168 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
174 /* xpm2rgb copied from nixieclock, which bore the legend:
176 nixieclock - a nixie desktop timepiece
177 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
179 and was released under the GPL.
183 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
185 static long vals[256], val;
186 uint32_t t, x, y, colors, cpp;
188 unsigned char *savergb, *rgb;
192 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
193 error << string_compose (_("bad XPM header %1"), xpm[0])
198 savergb = rgb = (unsigned char*) malloc (h * w * 3);
200 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
201 for (t = 0; t < colors; ++t) {
202 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
206 // COLORMAP -> RGB CONVERSION
207 // Get low 3 bytes from vals[]
211 for (y = h-1; y > 0; --y) {
213 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
214 val = vals[(int)*p++];
215 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
216 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
217 *(rgb+0) = val & 0xff; // 0:R
225 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
227 static long vals[256], val;
228 uint32_t t, x, y, colors, cpp;
230 unsigned char *savergb, *rgb;
235 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
236 error << string_compose (_("bad XPM header %1"), xpm[0])
241 savergb = rgb = (unsigned char*) malloc (h * w * 4);
243 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
245 if (strstr (xpm[1], "None")) {
246 sscanf (xpm[1], "%c", &transparent);
253 for (; t < colors; ++t) {
254 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
258 // COLORMAP -> RGB CONVERSION
259 // Get low 3 bytes from vals[]
263 for (y = h-1; y > 0; --y) {
267 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
269 if (transparent && (*p++ == transparent)) {
277 *(rgb+3) = alpha; // 3: alpha
278 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
279 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
280 *(rgb+0) = val & 0xff; // 0:R
287 ArdourCanvas::Points*
288 get_canvas_points (string /*who*/, uint32_t npoints)
290 // cerr << who << ": wants " << npoints << " canvas points" << endl;
291 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
292 if (npoints > (uint32_t) gdk_screen_width() + 4) {
296 return new ArdourCanvas::Points (npoints);
299 Pango::FontDescription*
300 get_font_for_style (string widgetname)
302 Gtk::Window window (WINDOW_TOPLEVEL);
304 Glib::RefPtr<Gtk::Style> style;
307 foobar.set_name (widgetname);
308 foobar.ensure_style();
310 style = foobar.get_style ();
312 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
314 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
318 /* layout inherited its font description from a PangoContext */
320 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
321 pfd = pango_context_get_font_description (ctxt);
322 return new Pango::FontDescription (pfd, true); /* make a copy */
325 return new Pango::FontDescription (pfd, true); /* make a copy */
329 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
331 /* In GTK+2, styles aren't set up correctly if the widget is not
332 attached to a toplevel window that has a screen pointer.
335 static Gtk::Window* window = 0;
338 window = new Window (WINDOW_TOPLEVEL);
345 foo.set_name (style);
348 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
352 r = waverc->fg[state].red / 257;
353 g = waverc->fg[state].green / 257;
354 b = waverc->fg[state].blue / 257;
356 /* what a hack ... "a" is for "active" */
357 if (state == Gtk::STATE_NORMAL && rgba) {
358 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
360 } else if (attr == "bg") {
362 r = waverc->bg[state].red / 257;
363 g = waverc->bg[state].green / 257;
364 b = waverc->bg[state].blue / 257;
365 } else if (attr == "base") {
366 r = waverc->base[state].red / 257;
367 g = waverc->base[state].green / 257;
368 b = waverc->base[state].blue / 257;
369 } else if (attr == "text") {
370 r = waverc->text[state].red / 257;
371 g = waverc->text[state].green / 257;
372 b = waverc->text[state].blue / 257;
375 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
380 if (state == Gtk::STATE_NORMAL && rgba) {
381 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
383 return (uint32_t) RGB_TO_UINT(r,g,b);
389 color_from_style (string widget_style_name, int state, string attr)
393 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
394 widget_style_name.c_str(),
398 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
399 return Gdk::Color ("red");
403 return Gdk::Color (&style->fg[state]);
407 return Gdk::Color (&style->bg[state]);
410 if (attr == "light") {
411 return Gdk::Color (&style->light[state]);
414 if (attr == "dark") {
415 return Gdk::Color (&style->dark[state]);
419 return Gdk::Color (&style->mid[state]);
422 if (attr == "text") {
423 return Gdk::Color (&style->text[state]);
426 if (attr == "base") {
427 return Gdk::Color (&style->base[state]);
430 if (attr == "text_aa") {
431 return Gdk::Color (&style->text_aa[state]);
434 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
435 return Gdk::Color ("red");
438 Glib::RefPtr<Gdk::GC>
439 gc_from_style (string widget_style_name, int state, string attr)
443 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
444 widget_style_name.c_str(),
448 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
449 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
450 ret->set_rgb_fg_color(Gdk::Color("red"));
455 return Glib::wrap(style->fg_gc[state]);
459 return Glib::wrap(style->bg_gc[state]);
462 if (attr == "light") {
463 return Glib::wrap(style->light_gc[state]);
466 if (attr == "dark") {
467 return Glib::wrap(style->dark_gc[state]);
471 return Glib::wrap(style->mid_gc[state]);
474 if (attr == "text") {
475 return Glib::wrap(style->text_gc[state]);
478 if (attr == "base") {
479 return Glib::wrap(style->base_gc[state]);
482 if (attr == "text_aa") {
483 return Glib::wrap(style->text_aa_gc[state]);
486 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
487 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
488 ret->set_rgb_fg_color(Gdk::Color("red"));
494 canvas_item_visible (ArdourCanvas::Item* item)
496 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
500 set_color (Gdk::Color& c, int rgb)
502 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
507 gboolean gdk_quartz_possibly_forward (GdkEvent*);
512 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
514 GtkWindow* win = window.gobj();
515 GtkWidget* focus = gtk_window_get_focus (win);
516 bool special_handling_of_unmodified_accelerators = false;
517 bool allow_activating = true;
519 #undef DEBUG_ACCELERATOR_HANDLING
520 #ifdef DEBUG_ACCELERATOR_HANDLING
521 //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
525 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
526 special_handling_of_unmodified_accelerators = true;
531 /* should this be universally true? */
532 if (Keyboard::some_magic_widget_has_focus ()) {
533 allow_activating = false;
537 #ifdef DEBUG_ACCELERATOR_HANDLING
539 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
540 << special_handling_of_unmodified_accelerators
541 << " magic widget focus ? "
542 << Keyboard::some_magic_widget_has_focus()
543 << " allow_activation ? "
549 /* This exists to allow us to override the way GTK handles
550 key events. The normal sequence is:
552 a) event is delivered to a GtkWindow
553 b) accelerators/mnemonics are activated
554 c) if (b) didn't handle the event, propagate to
555 the focus widget and/or focus chain
557 The problem with this is that if the accelerators include
558 keys without modifiers, such as the space bar or the
559 letter "e", then pressing the key while typing into
560 a text entry widget results in the accelerator being
561 activated, instead of the desired letter appearing
564 There is no good way of fixing this, but this
565 represents a compromise. The idea is that
566 key events involving modifiers (not Shift)
567 get routed into the activation pathway first, then
568 get propagated to the focus widget if necessary.
570 If the key event doesn't involve modifiers,
571 we deliver to the focus widget first, thus allowing
572 it to get "normal text" without interference
575 Of course, this can also be problematic: if there
576 is a widget with focus, then it will swallow
577 all "normal text" accelerators.
581 if (!special_handling_of_unmodified_accelerators) {
583 /* pretend that certain key events that GTK does not allow
584 to be used as accelerators are actually something that
588 uint32_t fakekey = ev->keyval;
590 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
591 if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
596 if (allow_activating) {
597 int oldval = ev->keyval;
598 ev->keyval = fakekey;
599 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
608 /* consider all relevant modifiers but not LOCK or SHIFT */
610 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
612 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
614 /* no special handling or there are modifiers in effect: accelerate first */
616 #ifdef DEBUG_ACCELERATOR_HANDLING
618 cerr << "\tactivate, then propagate\n";
622 if (allow_activating) {
624 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
628 if (gtk_window_activate_key (win, ev)) {
633 #ifdef DEBUG_ACCELERATOR_HANDLING
635 cerr << "\tnot accelerated, now propagate\n";
638 return gtk_window_propagate_key_event (win, ev);
641 /* no modifiers, propagate first */
643 #ifdef DEBUG_ACCELERATOR_HANDLING
645 cerr << "\tpropagate, then activate\n";
648 if (!gtk_window_propagate_key_event (win, ev)) {
649 #ifdef DEBUG_ACCELERATOR_HANDLING
651 cerr << "\tpropagation didn't handle, so activate\n";
655 if (allow_activating) {
658 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
662 return gtk_window_activate_key (win, ev);
666 #ifdef DEBUG_ACCELERATOR_HANDLING
668 cerr << "\thandled by propagate\n";
674 #ifdef DEBUG_ACCELERATOR_HANDLING
676 cerr << "\tnot handled\n";
682 Glib::RefPtr<Gdk::Pixbuf>
683 get_xpm (std::string name)
685 if (!xpm_map[name]) {
687 SearchPath spath(ARDOUR::ardour_search_path());
688 spath += ARDOUR::system_data_search_path();
690 spath.add_subdirectory_to_paths("pixmaps");
692 sys::path data_file_path;
694 if(!find_file_in_search_path (spath, name, data_file_path)) {
695 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
699 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
700 } catch(const Glib::Error& e) {
701 warning << "Caught Glib::Error: " << e.what() << endmsg;
705 return xpm_map[name];
709 Glib::RefPtr<Gdk::Pixbuf>
710 get_icon (const char* cname)
715 SearchPath spath(ARDOUR::ardour_search_path());
716 spath += ARDOUR::system_data_search_path();
718 spath.add_subdirectory_to_paths("icons");
720 sys::path data_file_path;
722 if (!find_file_in_search_path (spath, name, data_file_path)) {
723 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
726 Glib::RefPtr<Gdk::Pixbuf> img;
728 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
729 } catch (const Gdk::PixbufError &e) {
730 cerr << "Caught PixbufError: " << e.what() << endl;
732 g_message("Caught ... ");
739 longest (vector<string>& strings)
741 if (strings.empty()) {
745 vector<string>::iterator longest = strings.begin();
746 string::size_type longest_length = (*longest).length();
748 vector<string>::iterator i = longest;
751 while (i != strings.end()) {
753 string::size_type len = (*i).length();
755 if (len > longest_length) {
757 longest_length = len;
767 key_is_legal_for_numeric_entry (guint keyval)
785 case GDK_KP_Subtract:
814 set_pango_fontsize ()
816 long val = ARDOUR::Config->get_font_scale();
818 /* FT2 rendering - used by GnomeCanvas, sigh */
820 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
822 /* Cairo rendering, in case there is any */
824 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
830 long val = ARDOUR::Config->get_font_scale();
831 set_pango_fontsize ();
834 gtk_settings_set_long_property (gtk_settings_get_default(),
835 "gtk-xft-dpi", val, "ardour");
836 DPIReset();//Emit Signal
840 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
842 int fakekey = GDK_VoidSymbol;
846 case GDK_ISO_Left_Tab:
851 fakekey = GDK_uparrow;
855 fakekey = GDK_downarrow;
859 fakekey = GDK_rightarrow;
863 fakekey = GDK_leftarrow;
870 if (fakekey != GDK_VoidSymbol) {
880 convert_color_channel (guint8 src,
883 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
887 convert_bgra_to_rgba (guint8 const* src,
892 guint8 const* src_pixel = src;
893 guint8* dst_pixel = dst;
895 for (int y = 0; y < height; y++)
896 for (int x = 0; x < width; x++)
898 dst_pixel[0] = convert_color_channel (src_pixel[2],
900 dst_pixel[1] = convert_color_channel (src_pixel[1],
902 dst_pixel[2] = convert_color_channel (src_pixel[0],
904 dst_pixel[3] = src_pixel[3];