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>
40 #include <ardour/filesystem_paths.h>
42 #include "ardour_ui.h"
46 #include "rgb_macros.h"
47 #include "canvas_impl.h"
55 sigc::signal<void> DPIReset;
58 pixel_width (const ustring& str, Pango::FontDescription& font)
61 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
63 layout->set_font_description (font);
64 layout->set_text (str);
67 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
72 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
75 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
76 ustring::size_type shorter_by = 0;
79 layout->set_font_description (font);
84 ustring::iterator last = ustr.end();
85 --last; /* now points at final entry */
89 while (!ustr.empty()) {
91 layout->set_text (txt);
94 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
96 if (width < pixel_width) {
104 if (with_ellipses && shorter_by > 3) {
116 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
122 /* xpm2rgb copied from nixieclock, which bore the legend:
124 nixieclock - a nixie desktop timepiece
125 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
127 and was released under the GPL.
131 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
133 static long vals[256], val;
134 uint32_t t, x, y, colors, cpp;
136 unsigned char *savergb, *rgb;
140 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
141 error << string_compose (_("bad XPM header %1"), xpm[0])
146 savergb = rgb = (unsigned char*) malloc (h * w * 3);
148 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
149 for (t = 0; t < colors; ++t) {
150 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
154 // COLORMAP -> RGB CONVERSION
155 // Get low 3 bytes from vals[]
159 for (y = h-1; y > 0; --y) {
161 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
162 val = vals[(int)*p++];
163 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
164 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
165 *(rgb+0) = val & 0xff; // 0:R
173 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
175 static long vals[256], val;
176 uint32_t t, x, y, colors, cpp;
178 unsigned char *savergb, *rgb;
183 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
184 error << string_compose (_("bad XPM header %1"), xpm[0])
189 savergb = rgb = (unsigned char*) malloc (h * w * 4);
191 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
193 if (strstr (xpm[1], "None")) {
194 sscanf (xpm[1], "%c", &transparent);
201 for (; 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) {
215 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
217 if (transparent && (*p++ == transparent)) {
225 *(rgb+3) = alpha; // 3: alpha
226 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
227 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
228 *(rgb+0) = val & 0xff; // 0:R
235 ArdourCanvas::Points*
236 get_canvas_points (string who, uint32_t npoints)
238 // cerr << who << ": wants " << npoints << " canvas points" << endl;
239 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
240 if (npoints > (uint32_t) gdk_screen_width() + 4) {
244 return new ArdourCanvas::Points (npoints);
247 Pango::FontDescription*
248 get_font_for_style (string widgetname)
250 Gtk::Window window (WINDOW_TOPLEVEL);
252 Glib::RefPtr<Gtk::Style> style;
255 foobar.set_name (widgetname);
256 foobar.ensure_style();
258 style = foobar.get_style ();
260 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
262 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
266 /* layout inherited its font description from a PangoContext */
268 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
269 pfd = pango_context_get_font_description (ctxt);
270 return new Pango::FontDescription (pfd, true); /* make a copy */
273 return new Pango::FontDescription (pfd, true); /* make a copy */
277 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
279 /* In GTK+2, styles aren't set up correctly if the widget is not
280 attached to a toplevel window that has a screen pointer.
283 static Gtk::Window* window = 0;
286 window = new Window (WINDOW_TOPLEVEL);
293 foo.set_name (style);
296 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
300 r = waverc->fg[state].red / 257;
301 g = waverc->fg[state].green / 257;
302 b = waverc->fg[state].blue / 257;
304 /* what a hack ... "a" is for "active" */
305 if (state == Gtk::STATE_NORMAL && rgba) {
306 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
308 } else if (attr == "bg") {
310 r = waverc->bg[state].red / 257;
311 g = waverc->bg[state].green / 257;
312 b = waverc->bg[state].blue / 257;
313 } else if (attr == "base") {
314 r = waverc->base[state].red / 257;
315 g = waverc->base[state].green / 257;
316 b = waverc->base[state].blue / 257;
317 } else if (attr == "text") {
318 r = waverc->text[state].red / 257;
319 g = waverc->text[state].green / 257;
320 b = waverc->text[state].blue / 257;
323 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
328 if (state == Gtk::STATE_NORMAL && rgba) {
329 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
331 return (uint32_t) RGB_TO_UINT(r,g,b);
337 color_from_style (string widget_style_name, int state, string attr)
341 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
342 widget_style_name.c_str(),
346 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
347 return Gdk::Color ("red");
350 cerr << "got style for " << widget_style_name << endl;
353 return Gdk::Color (&style->fg[state]);
357 cerr << "returning color from bg\n";
358 return Gdk::Color (&style->bg[state]);
361 if (attr == "light") {
362 return Gdk::Color (&style->light[state]);
365 if (attr == "dark") {
366 return Gdk::Color (&style->dark[state]);
370 return Gdk::Color (&style->mid[state]);
373 if (attr == "text") {
374 return Gdk::Color (&style->text[state]);
377 if (attr == "base") {
378 return Gdk::Color (&style->base[state]);
381 if (attr == "text_aa") {
382 return Gdk::Color (&style->text_aa[state]);
385 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
386 return Gdk::Color ("red");
391 canvas_item_visible (ArdourCanvas::Item* item)
393 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
397 set_color (Gdk::Color& c, int rgb)
399 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
404 gboolean gdk_quartz_possibly_forward (GdkEvent*);
409 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
411 GtkWindow* win = window.gobj();
412 GtkWidget* focus = gtk_window_get_focus (win);
413 bool special_handling_of_unmodified_accelerators = false;
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;
425 #ifdef DEBUG_ACCELERATOR_HANDLING
427 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
428 << special_handling_of_unmodified_accelerators
433 /* This exists to allow us to override the way GTK handles
434 key events. The normal sequence is:
436 a) event is delivered to a GtkWindow
437 b) accelerators/mnemonics are activated
438 c) if (b) didn't handle the event, propagate to
439 the focus widget and/or focus chain
441 The problem with this is that if the accelerators include
442 keys without modifiers, such as the space bar or the
443 letter "e", then pressing the key while typing into
444 a text entry widget results in the accelerator being
445 activated, instead of the desired letter appearing
448 There is no good way of fixing this, but this
449 represents a compromise. The idea is that
450 key events involving modifiers (not Shift)
451 get routed into the activation pathway first, then
452 get propagated to the focus widget if necessary.
454 If the key event doesn't involve modifiers,
455 we deliver to the focus widget first, thus allowing
456 it to get "normal text" without interference
459 Of course, this can also be problematic: if there
460 is a widget with focus, then it will swallow
461 all "normal text" accelerators.
465 if (!special_handling_of_unmodified_accelerators) {
467 /* pretend that certain key events that GTK does not allow
468 to be used as accelerators are actually something that
472 int fakekey = GDK_VoidSymbol;
475 switch (ev->keyval) {
477 case GDK_ISO_Left_Tab:
482 fakekey = GDK_uparrow;
486 fakekey = GDK_downarrow;
490 fakekey = GDK_rightarrow;
494 fakekey = GDK_leftarrow;
501 if (fakekey != GDK_VoidSymbol) {
502 ret = gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state));
509 int oldval = ev->keyval;
510 ev->keyval = fakekey;
511 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
519 /* consider all relevant modifiers but not LOCK or SHIFT */
521 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
523 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
525 /* no special handling or there are modifiers in effect: accelerate first */
527 #ifdef DEBUG_ACCELERATOR_HANDLING
529 cerr << "\tactivate, then propagate\n";
533 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
537 if (!gtk_window_activate_key (win, ev)) {
538 #ifdef DEBUG_ACCELERATOR_HANDLING
540 cerr << "\tnot accelerated, now propagate\n";
543 return gtk_window_propagate_key_event (win, ev);
545 #ifdef DEBUG_ACCELERATOR_HANDLING
547 cerr << "\taccelerated - done.\n";
554 /* no modifiers, propagate first */
556 #ifdef DEBUG_ACCELERATOR_HANDLING
558 cerr << "\tpropagate, then activate\n";
561 if (!gtk_window_propagate_key_event (win, ev)) {
562 #ifdef DEBUG_ACCELERATOR_HANDLING
564 cerr << "\tpropagation didn't handle, so activate\n";
568 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
572 return gtk_window_activate_key (win, ev);
574 #ifdef DEBUG_ACCELERATOR_HANDLING
576 cerr << "\thandled by propagate\n";
582 #ifdef DEBUG_ACCELERATOR_HANDLING
584 cerr << "\tnot handled\n";
590 Glib::RefPtr<Gdk::Pixbuf>
591 get_xpm (std::string name)
593 SearchPath spath(ARDOUR::ardour_search_path());
594 spath += ARDOUR::system_data_search_path();
596 spath.add_subdirectory_to_paths("pixmaps");
598 sys::path data_file_path;
600 if(!find_file_in_search_path (spath, name, data_file_path)) {
601 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
604 return Gdk::Pixbuf::create_from_file (data_file_path.to_string());
607 Glib::RefPtr<Gdk::Pixbuf>
608 get_icon (const char* cname)
613 SearchPath spath(ARDOUR::ardour_search_path());
614 spath += ARDOUR::system_data_search_path();
616 spath.add_subdirectory_to_paths("icons");
618 sys::path data_file_path;
620 if (!find_file_in_search_path (spath, name, data_file_path)) {
621 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
624 Glib::RefPtr<Gdk::Pixbuf> img;
626 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
628 catch (const Gdk::PixbufError &e)
630 cerr << "Caught PixbufError: " << e.what() << endl;
634 g_message("Caught ... ");
641 longest (vector<string>& strings)
643 if (strings.empty()) {
647 vector<string>::iterator longest = strings.begin();
648 string::size_type longest_length = (*longest).length();
650 vector<string>::iterator i = longest;
653 while (i != strings.end()) {
655 string::size_type len = (*i).length();
657 if (len > longest_length) {
659 longest_length = len;
669 key_is_legal_for_numeric_entry (guint keyval)
687 case GDK_KP_Subtract:
716 set_pango_fontsize ()
718 long val = ARDOUR::Config->get_font_scale();
722 pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
724 /* Cairo rendering, in case there is any */
726 pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
732 long val = ARDOUR::Config->get_font_scale();
733 set_pango_fontsize ();
736 gtk_settings_set_long_property (gtk_settings_get_default(),
737 "gtk-xft-dpi", val, "ardour");
738 DPIReset();//Emit Signal