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
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 <gtkmm2ext/utils.h>
36 #include <ardour/ardour.h>
37 #include <ardour/configuration.h>
39 #include "ardour_ui.h"
43 #include "rgb_macros.h"
44 #include "canvas_impl.h"
52 sigc::signal<void> DPIReset;
55 pixel_width (const ustring& str, Pango::FontDescription& font)
58 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
60 layout->set_font_description (font);
61 layout->set_text (str);
64 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
69 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
72 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
73 ustring::size_type shorter_by = 0;
76 layout->set_font_description (font);
81 ustring::iterator last = ustr.end();
82 --last; /* now points at final entry */
86 while (!ustr.empty()) {
88 layout->set_text (txt);
91 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
93 if (width < pixel_width) {
101 if (with_ellipses && shorter_by > 3) {
113 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
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.
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;
133 unsigned char *savergb, *rgb;
137 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
138 error << string_compose (_("bad XPM header %1"), xpm[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);
151 // COLORMAP -> RGB CONVERSION
152 // Get low 3 bytes from vals[]
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
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;
175 unsigned char *savergb, *rgb;
180 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
181 error << string_compose (_("bad XPM header %1"), xpm[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);
198 for (; t < colors; ++t) {
199 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
203 // COLORMAP -> RGB CONVERSION
204 // Get low 3 bytes from vals[]
208 for (y = h-1; y > 0; --y) {
212 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
214 if (transparent && (*p++ == transparent)) {
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
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) {
241 return new ArdourCanvas::Points (npoints);
244 Pango::FontDescription*
245 get_font_for_style (string widgetname)
247 Gtk::Window window (WINDOW_TOPLEVEL);
249 Glib::RefPtr<Gtk::Style> style;
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());
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 */
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;
283 window = new Window (WINDOW_TOPLEVEL);
290 foo.set_name (style);
293 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
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") {
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;
320 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
325 if (state == Gtk::STATE_NORMAL && rgba) {
326 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
328 return (uint32_t) RGB_TO_UINT(r,g,b);
334 color_from_style (string widget_style_name, int state, string attr)
338 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
339 widget_style_name.c_str(),
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;
350 return Gdk::Color (&style->fg[state]);
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]);
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");
388 canvas_item_visible (ArdourCanvas::Item* item)
390 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
394 set_color (Gdk::Color& c, int rgb)
396 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
401 gboolean gdk_quartz_possibly_forward (GdkEvent*);
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;
412 bool allow_forwarding = true;
415 #undef DEBUG_ACCELERATOR_HANDLING
416 #ifdef DEBUG_ACCELERATOR_HANDLING
417 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
420 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
421 special_handling_of_unmodified_accelerators = true;
426 if (Keyboard::some_magic_widget_has_focus ()) {
427 allow_forwarding = false;
431 #ifdef DEBUG_ACCELERATOR_HANDLING
433 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
434 << special_handling_of_unmodified_accelerators
439 /* This exists to allow us to override the way GTK handles
440 key events. The normal sequence is:
442 a) event is delivered to a GtkWindow
443 b) accelerators/mnemonics are activated
444 c) if (b) didn't handle the event, propagate to
445 the focus widget and/or focus chain
447 The problem with this is that if the accelerators include
448 keys without modifiers, such as the space bar or the
449 letter "e", then pressing the key while typing into
450 a text entry widget results in the accelerator being
451 activated, instead of the desired letter appearing
454 There is no good way of fixing this, but this
455 represents a compromise. The idea is that
456 key events involving modifiers (not Shift)
457 get routed into the activation pathway first, then
458 get propagated to the focus widget if necessary.
460 If the key event doesn't involve modifiers,
461 we deliver to the focus widget first, thus allowing
462 it to get "normal text" without interference
465 Of course, this can also be problematic: if there
466 is a widget with focus, then it will swallow
467 all "normal text" accelerators.
471 if (!special_handling_of_unmodified_accelerators) {
473 /* pretend that certain key events that GTK does not allow
474 to be used as accelerators are actually something that
478 uint32_t fakekey = ev->keyval;
480 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
481 if (gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
486 int oldval = ev->keyval;
487 ev->keyval = fakekey;
488 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
496 /* consider all relevant modifiers but not LOCK or SHIFT */
498 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
500 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
502 /* no special handling or there are modifiers in effect: accelerate first */
504 #ifdef DEBUG_ACCELERATOR_HANDLING
506 cerr << "\tactivate, then propagate\n";
511 if (allow_forwarding && gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
515 if (!gtk_window_activate_key (win, ev)) {
516 #ifdef DEBUG_ACCELERATOR_HANDLING
518 cerr << "\tnot accelerated, now propagate\n";
521 return gtk_window_propagate_key_event (win, ev);
523 #ifdef DEBUG_ACCELERATOR_HANDLING
525 cerr << "\taccelerated - done.\n";
532 /* no modifiers, propagate first */
534 #ifdef DEBUG_ACCELERATOR_HANDLING
536 cerr << "\tpropagate, then activate\n";
539 if (!gtk_window_propagate_key_event (win, ev)) {
540 #ifdef DEBUG_ACCELERATOR_HANDLING
542 cerr << "\tpropagation didn't handle, so activate\n";
546 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
550 return gtk_window_activate_key (win, ev);
552 #ifdef DEBUG_ACCELERATOR_HANDLING
554 cerr << "\thandled by propagate\n";
560 #ifdef DEBUG_ACCELERATOR_HANDLING
562 cerr << "\tnot handled\n";
568 Glib::RefPtr<Gdk::Pixbuf>
569 get_xpm (std::string name)
571 //cerr << "xpm path = " << ARDOUR::find_data_file(name, "pixmaps") << endl;//DEBUG
572 if (!xpm_map[name]) {
574 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
576 catch(const Glib::Error& e) {
577 warning << "Caught Glib::Error: " << e.what() << endmsg;
581 return (xpm_map[name]);
584 Glib::RefPtr<Gdk::Pixbuf>
585 get_icon (const char* cname)
590 string path = ARDOUR::find_data_file (name, "icons");
593 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
597 Glib::RefPtr<Gdk::Pixbuf> img;
599 img = Gdk::Pixbuf::create_from_file (path);
601 catch (const Gdk::PixbufError &e)
603 cerr << "Caught PixbufError: " << e.what() << endl;
607 g_message("Caught ... ");
614 longest (vector<string>& strings)
616 if (strings.empty()) {
620 vector<string>::iterator longest = strings.begin();
621 string::size_type longest_length = (*longest).length();
623 vector<string>::iterator i = longest;
626 while (i != strings.end()) {
628 string::size_type len = (*i).length();
630 if (len > longest_length) {
632 longest_length = len;
642 key_is_legal_for_numeric_entry (guint keyval)
660 case GDK_KP_Subtract:
689 set_pango_fontsize ()
691 long val = ARDOUR::Config->get_font_scale();
693 /* FT2 rendering - used by GnomeCanvas, sigh */
695 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
697 /* Cairo rendering, in case there is any */
699 // pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
705 long val = ARDOUR::Config->get_font_scale();
706 set_pango_fontsize ();
709 gtk_settings_set_long_property (gtk_settings_get_default(),
710 "gtk-xft-dpi", val, "ardour");
711 DPIReset();//Emit Signal
715 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
717 int fakekey = GDK_VoidSymbol;
721 case GDK_ISO_Left_Tab:
726 fakekey = GDK_uparrow;
730 fakekey = GDK_downarrow;
734 fakekey = GDK_rightarrow;
738 fakekey = GDK_leftarrow;
745 if (fakekey != GDK_VoidSymbol) {