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 <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");
351 cerr << "got style for " << widget_style_name << endl;
354 return Gdk::Color (&style->fg[state]);
358 cerr << "returning color from bg\n";
359 return Gdk::Color (&style->bg[state]);
362 if (attr == "light") {
363 return Gdk::Color (&style->light[state]);
366 if (attr == "dark") {
367 return Gdk::Color (&style->dark[state]);
371 return Gdk::Color (&style->mid[state]);
374 if (attr == "text") {
375 return Gdk::Color (&style->text[state]);
378 if (attr == "base") {
379 return Gdk::Color (&style->base[state]);
382 if (attr == "text_aa") {
383 return Gdk::Color (&style->text_aa[state]);
386 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
387 return Gdk::Color ("red");
392 canvas_item_visible (ArdourCanvas::Item* item)
394 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
398 set_color (Gdk::Color& c, int rgb)
400 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
405 gboolean gdk_quartz_possibly_forward (GdkEvent*);
410 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
412 GtkWindow* win = window.gobj();
413 GtkWidget* focus = gtk_window_get_focus (win);
414 bool special_handling_of_unmodified_accelerators = false;
416 #undef DEBUG_ACCELERATOR_HANDLING
417 #ifdef DEBUG_ACCELERATOR_HANDLING
418 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
421 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
422 special_handling_of_unmodified_accelerators = true;
426 #ifdef DEBUG_ACCELERATOR_HANDLING
428 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
429 << special_handling_of_unmodified_accelerators
434 /* This exists to allow us to override the way GTK handles
435 key events. The normal sequence is:
437 a) event is delivered to a GtkWindow
438 b) accelerators/mnemonics are activated
439 c) if (b) didn't handle the event, propagate to
440 the focus widget and/or focus chain
442 The problem with this is that if the accelerators include
443 keys without modifiers, such as the space bar or the
444 letter "e", then pressing the key while typing into
445 a text entry widget results in the accelerator being
446 activated, instead of the desired letter appearing
449 There is no good way of fixing this, but this
450 represents a compromise. The idea is that
451 key events involving modifiers (not Shift)
452 get routed into the activation pathway first, then
453 get propagated to the focus widget if necessary.
455 If the key event doesn't involve modifiers,
456 we deliver to the focus widget first, thus allowing
457 it to get "normal text" without interference
460 Of course, this can also be problematic: if there
461 is a widget with focus, then it will swallow
462 all "normal text" accelerators.
466 if (!special_handling_of_unmodified_accelerators) {
468 /* pretend that certain key events that GTK does not allow
469 to be used as accelerators are actually something that
473 uint32_t fakekey = ev->keyval;
475 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
476 if (gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
481 int oldval = ev->keyval;
482 ev->keyval = fakekey;
483 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
491 /* consider all relevant modifiers but not LOCK or SHIFT */
493 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
495 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
497 /* no special handling or there are modifiers in effect: accelerate first */
499 #ifdef DEBUG_ACCELERATOR_HANDLING
501 cerr << "\tactivate, then propagate\n";
505 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
509 if (!gtk_window_activate_key (win, ev)) {
510 #ifdef DEBUG_ACCELERATOR_HANDLING
512 cerr << "\tnot accelerated, now propagate\n";
515 return gtk_window_propagate_key_event (win, ev);
517 #ifdef DEBUG_ACCELERATOR_HANDLING
519 cerr << "\taccelerated - done.\n";
526 /* no modifiers, propagate first */
528 #ifdef DEBUG_ACCELERATOR_HANDLING
530 cerr << "\tpropagate, then activate\n";
533 if (!gtk_window_propagate_key_event (win, ev)) {
534 #ifdef DEBUG_ACCELERATOR_HANDLING
536 cerr << "\tpropagation didn't handle, so activate\n";
540 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
544 return gtk_window_activate_key (win, ev);
546 #ifdef DEBUG_ACCELERATOR_HANDLING
548 cerr << "\thandled by propagate\n";
554 #ifdef DEBUG_ACCELERATOR_HANDLING
556 cerr << "\tnot handled\n";
562 Glib::RefPtr<Gdk::Pixbuf>
563 get_xpm (std::string name)
565 if (!xpm_map[name]) {
567 SearchPath spath(ARDOUR::ardour_search_path());
568 spath += ARDOUR::system_data_search_path();
570 spath.add_subdirectory_to_paths("pixmaps");
572 sys::path data_file_path;
574 if (!find_file_in_search_path (spath, name, data_file_path)) {
575 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
580 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
583 catch(const Glib::Error& e) {
584 warning << "Caught Glib::Error: " << e.what() << endmsg;
588 return xpm_map[name];
591 Glib::RefPtr<Gdk::Pixbuf>
592 get_icon (const char* cname)
597 SearchPath spath(ARDOUR::ardour_search_path());
598 spath += ARDOUR::system_data_search_path();
600 spath.add_subdirectory_to_paths("icons");
602 sys::path data_file_path;
604 if (!find_file_in_search_path (spath, name, data_file_path)) {
605 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
608 Glib::RefPtr<Gdk::Pixbuf> img;
610 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
612 catch (const Gdk::PixbufError &e)
614 cerr << "Caught PixbufError: " << e.what() << endl;
618 g_message("Caught ... ");
625 longest (vector<string>& strings)
627 if (strings.empty()) {
631 vector<string>::iterator longest = strings.begin();
632 string::size_type longest_length = (*longest).length();
634 vector<string>::iterator i = longest;
637 while (i != strings.end()) {
639 string::size_type len = (*i).length();
641 if (len > longest_length) {
643 longest_length = len;
653 key_is_legal_for_numeric_entry (guint keyval)
671 case GDK_KP_Subtract:
700 set_pango_fontsize ()
702 long val = ARDOUR::Config->get_font_scale();
706 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
708 /* Cairo rendering, in case there is any */
710 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
716 long val = ARDOUR::Config->get_font_scale();
717 set_pango_fontsize ();
720 gtk_settings_set_long_property (gtk_settings_get_default(),
721 "gtk-xft-dpi", val, "ardour");
722 DPIReset();//Emit Signal
726 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
728 int fakekey = GDK_VoidSymbol;
732 case GDK_ISO_Left_Tab:
737 fakekey = GDK_uparrow;
741 fakekey = GDK_downarrow;
745 fakekey = GDK_rightarrow;
749 fakekey = GDK_leftarrow;
756 if (fakekey != GDK_VoidSymbol) {