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
)
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);
404 relay_key_press (GdkEventKey
* ev
, Gtk::Window
* win
)
406 if (!key_press_focus_accelerator_handler (*win
, ev
)) {
407 return PublicEditor::instance().on_key_press_event(ev
);
415 osx_keyval_without_alt (guint accent_keyval
)
417 switch (accent_keyval
) {
432 case GDK_leftdoublequotemark
:
433 return GDK_bracketleft
;
434 case GDK_leftsinglequotemark
:
435 return GDK_bracketright
;
436 case GDK_guillemotleft
:
437 return GDK_backslash
;
442 case GDK_partialderivative
:
453 return GDK_semicolon
;
455 return GDK_apostrophe
;
456 case GDK_Greek_OMEGA
:
466 case GDK_lessthanequal
:
468 case GDK_greaterthanequal
:
476 return GDK_VoidSymbol
;
481 key_press_focus_accelerator_handler (Gtk::Window
& window
, GdkEventKey
* ev
)
483 GtkWindow
* win
= window
.gobj();
484 GtkWidget
* focus
= gtk_window_get_focus (win
);
485 bool special_handling_of_unmodified_accelerators
= false;
486 bool allow_activating
= true;
488 //#define DEBUG_ACCELERATOR_HANDLING
489 #ifdef DEBUG_ACCELERATOR_HANDLING
490 //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
494 if (GTK_IS_ENTRY(focus
) || Keyboard::some_magic_widget_has_focus()) {
495 special_handling_of_unmodified_accelerators
= true;
497 #ifdef DEBUG_ACCELERATOR_HANDLING
498 cerr
<< "Focus widget name " << gtk_widget_get_name(focus
) << endl
;
503 /* should this be universally true? */
504 if (Keyboard::some_magic_widget_has_focus ()) {
505 allow_activating
= false;
509 #ifdef DEBUG_ACCELERATOR_HANDLING
511 cerr
<< "Win = " << win
<< " Key event: code = " << ev
->keyval
<< " name = " << gdk_keyval_name (ev
->keyval
) << " state = " << hex
<< ev
->state
<< dec
512 << " ctrl " << ((ev
->state
& GDK_CONTROL_MASK
) ? 1 : 0)
513 << " alt " << ((ev
->state
& GDK_MOD1_MASK
) ? 1 : 0)
514 << " shift " << ((ev
->state
& GDK_SHIFT_MASK
) ? 1 : 0)
515 << " cmd/meta " << ((ev
->state
& GDK_META_MASK
) ? 1 : 0)
516 << " lock " << ((ev
->state
& GDK_LOCK_MASK
) ? 1 : 0)
517 << " 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
) {
560 if (ev
->state
& GDK_MOD1_MASK
) {
561 /* we're not in a text entry or "magic focus" widget so we don't want OS X "special-character"
562 text-style handling of alt-<key>. change the keyval back to what it would be without
563 the alt key. this way, we see <alt>-v rather than <alt>-radical and so on.
565 guint keyval_without_alt
= osx_keyval_without_alt (ev
->keyval
);
567 if (keyval_without_alt
!= GDK_VoidSymbol
) {
568 #ifdef DEBUG_ACCELERATOR_HANDLING
569 cerr
<< "Remapped " << gdk_keyval_name (ev
->keyval
) << " to " << gdk_keyval_name (keyval_without_alt
) << endl
;
571 #endif ev->keyval = keyval_without_alt;
577 if (!special_handling_of_unmodified_accelerators
) {
579 /* pretend that certain key events that GTK does not allow
580 to be used as accelerators are actually something that
584 uint32_t fakekey
= ev
->keyval
;
586 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey
)) {
587 #ifdef DEBUG_ACCELERATOR_HANDLING
589 cerr
<< "\tactivate (was " << ev
->keyval
<< " now " << fakekey
<< ") without special handling of unmodified accels\n";
592 if (allow_activating
&& gtk_accel_groups_activate(G_OBJECT(win
), fakekey
, GdkModifierType(ev
->state
))) {
593 #ifdef DEBUG_ACCELERATOR_HANDLING
595 cerr
<< "\tactivation handled key\n";
603 /* consider all relevant modifiers but not LOCK or SHIFT */
605 guint mask
= (Keyboard::RelevantModifierKeyMask
& ~(Gdk::SHIFT_MASK
|Gdk::LOCK_MASK
));
607 if (!special_handling_of_unmodified_accelerators
|| (ev
->state
& mask
)) {
609 /* no special handling or there are modifiers in effect: accelerate first */
611 #ifdef DEBUG_ACCELERATOR_HANDLING
613 cerr
<< "\tactivate, then propagate\n";
617 if (allow_activating
) {
618 if (gtk_window_activate_key (win
, ev
)) {
619 #ifdef DEBUG_ACCELERATOR_HANDLING
621 cerr
<< "\tactivation handled key " << ev
->keyval
<< endl
;
629 #ifdef DEBUG_ACCELERATOR_HANDLING
631 cerr
<< "\tnot accelerated, now propagate\n";
634 return gtk_window_propagate_key_event (win
, ev
);
637 /* no modifiers, propagate first */
639 #ifdef DEBUG_ACCELERATOR_HANDLING
641 cerr
<< "\tpropagate, then activate\n";
644 if (!gtk_window_propagate_key_event (win
, ev
)) {
645 #ifdef DEBUG_ACCELERATOR_HANDLING
647 cerr
<< "\tpropagation didn't handle, so activate\n";
651 if (allow_activating
) {
652 return gtk_window_activate_key (win
, ev
);
656 #ifdef DEBUG_ACCELERATOR_HANDLING
658 cerr
<< "\thandled by propagate\n";
664 #ifdef DEBUG_ACCELERATOR_HANDLING
666 cerr
<< "\tnot handled\n";
672 Glib::RefPtr
<Gdk::Pixbuf
>
673 get_xpm (std::string name
)
675 //cerr << "xpm path = " << ARDOUR::find_data_file(name, "pixmaps") << endl;//DEBUG
676 if (!xpm_map
[name
]) {
678 xpm_map
[name
] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name
, "pixmaps"));
680 catch(const Glib::Error
& e
) {
681 warning
<< "Caught Glib::Error: " << e
.what() << endmsg
;
685 return (xpm_map
[name
]);
688 Glib::RefPtr
<Gdk::Pixbuf
>
689 get_icon (const char* cname
)
694 string path
= ARDOUR::find_data_file (name
, "icons");
697 fatal
<< string_compose (_("cannot find icon image for %1"), name
) << endmsg
;
701 Glib::RefPtr
<Gdk::Pixbuf
> img
;
703 img
= Gdk::Pixbuf::create_from_file (path
);
705 catch (const Gdk::PixbufError
&e
)
707 cerr
<< "Caught PixbufError: " << e
.what() << endl
;
711 g_message("Caught ... ");
718 longest (vector
<string
>& strings
)
720 if (strings
.empty()) {
724 vector
<string
>::iterator longest
= strings
.begin();
725 string::size_type longest_length
= (*longest
).length();
727 vector
<string
>::iterator i
= longest
;
730 while (i
!= strings
.end()) {
732 string::size_type len
= (*i
).length();
734 if (len
> longest_length
) {
736 longest_length
= len
;
746 key_is_legal_for_numeric_entry (guint keyval
)
764 case GDK_KP_Subtract
:
793 set_pango_fontsize ()
795 long val
= ARDOUR::Config
->get_font_scale();
797 /* FT2 rendering - used by GnomeCanvas, sigh */
799 pango_ft2_font_map_set_resolution ((PangoFT2FontMap
*) pango_ft2_font_map_for_display(), val
/1024, val
/1024);
801 /* Cairo rendering, in case there is any */
803 // pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
809 long val
= ARDOUR::Config
->get_font_scale();
810 set_pango_fontsize ();
813 gtk_settings_set_long_property (gtk_settings_get_default(),
814 "gtk-xft-dpi", val
, "ardour");
815 DPIReset();//Emit Signal
819 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval
)
821 int fakekey
= GDK_VoidSymbol
;
825 case GDK_ISO_Left_Tab
:
830 fakekey
= GDK_uparrow
;
834 fakekey
= GDK_downarrow
;
838 fakekey
= GDK_rightarrow
;
842 fakekey
= GDK_leftarrow
;
849 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];
889 Glib::RefPtr
<Gdk::Pixbuf
>
890 pixbuf_from_ustring(const ustring
& name
, Pango::FontDescription
* font
, uint32_t rgba
, int clip_width
, int clip_height
)
892 Glib::RefPtr
<Gdk::Pixbuf
> buf
= Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB
, true, 8, clip_width
, clip_height
);
893 cairo_surface_t
* surface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, clip_width
, clip_height
);
894 cairo_t
* cr
= cairo_create (surface
);
895 cairo_text_extents_t te
;
897 float fr
, fg
, fb
, fa
;
899 fr
= ((rgba
& 0xff000000) >> 24) / 255.0f
;
900 fg
= ((rgba
& 0xff0000) >> 16) / 255.0f
;
901 fb
= ((rgba
& 0xff00) >> 8) / 255.0f
;
902 fa
= (rgba
& 0xff) / 255.0f
;
904 cairo_set_source_rgba (cr
, fr
, fg
, fb
, fa
);
905 cairo_select_font_face (cr
, font
->get_family().c_str(),
906 CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
907 cairo_set_font_size (cr
, font
->get_size() / Pango::SCALE
);
908 cairo_text_extents (cr
, name
.c_str(), &te
);
910 cairo_move_to (cr
, 0.5, 0.5 - te
.height
/ 2 - te
.y_bearing
+ clip_height
/ 2);
911 cairo_show_text (cr
, name
.c_str());
913 convert_bgra_to_rgba(cairo_image_surface_get_data (surface
), buf
->get_pixels(), clip_width
, clip_height
);
916 cairo_surface_destroy(surface
);