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.
24 #include <libart_lgpl/art_misc.h>
26 #include <gtkmm/window.h>
27 #include <gtkmm/combo.h>
28 #include <gtkmm/label.h>
29 #include <gtkmm/paned.h>
30 #include <gtk/gtkpaned.h>
32 #include <pbd/file_utils.h>
34 #include <gtkmm2ext/utils.h>
36 #include <ardour/filesystem_paths.h>
38 #include "ardour_ui.h"
42 #include "rgb_macros.h"
43 #include "canvas_impl.h"
52 pixel_width (const ustring& str, Pango::FontDescription& font)
55 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
57 layout->set_font_description (font);
58 layout->set_text (str);
61 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
66 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
69 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
70 ustring::size_type shorter_by = 0;
73 layout->set_font_description (font);
78 ustring::iterator last = ustr.end();
79 --last; /* now points at final entry */
83 while (!ustr.empty()) {
85 layout->set_text (txt);
88 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
90 if (width < pixel_width) {
98 if (with_ellipses && shorter_by > 3) {
110 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
116 /* xpm2rgb copied from nixieclock, which bore the legend:
118 nixieclock - a nixie desktop timepiece
119 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
121 and was released under the GPL.
125 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
127 static long vals[256], val;
128 uint32_t t, x, y, colors, cpp;
130 unsigned char *savergb, *rgb;
134 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
135 error << string_compose (_("bad XPM header %1"), xpm[0])
140 savergb = rgb = (unsigned char*) malloc (h * w * 3);
142 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
143 for (t = 0; t < colors; ++t) {
144 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
148 // COLORMAP -> RGB CONVERSION
149 // Get low 3 bytes from vals[]
153 for (y = h-1; y > 0; --y) {
155 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
156 val = vals[(int)*p++];
157 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
158 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
159 *(rgb+0) = val & 0xff; // 0:R
167 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
169 static long vals[256], val;
170 uint32_t t, x, y, colors, cpp;
172 unsigned char *savergb, *rgb;
177 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
178 error << string_compose (_("bad XPM header %1"), xpm[0])
183 savergb = rgb = (unsigned char*) malloc (h * w * 4);
185 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
187 if (strstr (xpm[1], "None")) {
188 sscanf (xpm[1], "%c", &transparent);
195 for (; t < colors; ++t) {
196 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
200 // COLORMAP -> RGB CONVERSION
201 // Get low 3 bytes from vals[]
205 for (y = h-1; y > 0; --y) {
209 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
211 if (transparent && (*p++ == transparent)) {
219 *(rgb+3) = alpha; // 3: alpha
220 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
221 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
222 *(rgb+0) = val & 0xff; // 0:R
229 ArdourCanvas::Points*
230 get_canvas_points (string who, uint32_t npoints)
232 // cerr << who << ": wants " << npoints << " canvas points" << endl;
233 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
234 if (npoints > (uint32_t) gdk_screen_width() + 4) {
238 return new ArdourCanvas::Points (npoints);
241 Pango::FontDescription*
242 get_font_for_style (string widgetname)
244 Gtk::Window window (WINDOW_TOPLEVEL);
246 Glib::RefPtr<Gtk::Style> style;
249 foobar.set_name (widgetname);
250 foobar.ensure_style();
252 style = foobar.get_style ();
254 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
256 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
260 /* layout inherited its font description from a PangoContext */
262 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
263 pfd = pango_context_get_font_description (ctxt);
264 return new Pango::FontDescription (pfd, true); /* make a copy */
267 return new Pango::FontDescription (pfd, true); /* make a copy */
271 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
273 /* In GTK+2, styles aren't set up correctly if the widget is not
274 attached to a toplevel window that has a screen pointer.
277 static Gtk::Window* window = 0;
280 window = new Window (WINDOW_TOPLEVEL);
287 foo.set_name (style);
290 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
294 r = waverc->fg[state].red / 257;
295 g = waverc->fg[state].green / 257;
296 b = waverc->fg[state].blue / 257;
298 /* what a hack ... "a" is for "active" */
299 if (state == Gtk::STATE_NORMAL && rgba) {
300 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
302 } else if (attr == "bg") {
304 r = waverc->bg[state].red / 257;
305 g = waverc->bg[state].green / 257;
306 b = waverc->bg[state].blue / 257;
307 } else if (attr == "base") {
308 r = waverc->base[state].red / 257;
309 g = waverc->base[state].green / 257;
310 b = waverc->base[state].blue / 257;
311 } else if (attr == "text") {
312 r = waverc->text[state].red / 257;
313 g = waverc->text[state].green / 257;
314 b = waverc->text[state].blue / 257;
317 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
322 if (state == Gtk::STATE_NORMAL && rgba) {
323 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
325 return (uint32_t) RGB_TO_UINT(r,g,b);
331 color_from_style (string widget_style_name, int state, string attr)
335 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
336 widget_style_name.c_str(),
340 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
341 return Gdk::Color ("red");
344 cerr << "got style for " << widget_style_name << endl;
347 return Gdk::Color (&style->fg[state]);
351 cerr << "returning color from bg\n";
352 return Gdk::Color (&style->bg[state]);
355 if (attr == "light") {
356 return Gdk::Color (&style->light[state]);
359 if (attr == "dark") {
360 return Gdk::Color (&style->dark[state]);
364 return Gdk::Color (&style->mid[state]);
367 if (attr == "text") {
368 return Gdk::Color (&style->text[state]);
371 if (attr == "base") {
372 return Gdk::Color (&style->base[state]);
375 if (attr == "text_aa") {
376 return Gdk::Color (&style->text_aa[state]);
379 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
380 return Gdk::Color ("red");
385 canvas_item_visible (ArdourCanvas::Item* item)
387 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
391 set_color (Gdk::Color& c, int rgb)
393 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
398 gboolean gdk_quartz_possibly_forward (GdkEvent*);
403 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
405 GtkWindow* win = window.gobj();
406 GtkWidget* focus = gtk_window_get_focus (win);
407 bool special_handling_of_unmodified_accelerators = false;
409 #undef DEBUG_ACCELERATOR_HANDLING
410 #ifdef DEBUG_ACCELERATOR_HANDLING
411 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
414 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
415 special_handling_of_unmodified_accelerators = true;
419 #ifdef DEBUG_ACCELERATOR_HANDLING
421 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
422 << special_handling_of_unmodified_accelerators
427 /* This exists to allow us to override the way GTK handles
428 key events. The normal sequence is:
430 a) event is delivered to a GtkWindow
431 b) accelerators/mnemonics are activated
432 c) if (b) didn't handle the event, propagate to
433 the focus widget and/or focus chain
435 The problem with this is that if the accelerators include
436 keys without modifiers, such as the space bar or the
437 letter "e", then pressing the key while typing into
438 a text entry widget results in the accelerator being
439 activated, instead of the desired letter appearing
442 There is no good way of fixing this, but this
443 represents a compromise. The idea is that
444 key events involving modifiers (not Shift)
445 get routed into the activation pathway first, then
446 get propagated to the focus widget if necessary.
448 If the key event doesn't involve modifiers,
449 we deliver to the focus widget first, thus allowing
450 it to get "normal text" without interference
453 Of course, this can also be problematic: if there
454 is a widget with focus, then it will swallow
455 all "normal text" accelerators.
459 if (!special_handling_of_unmodified_accelerators) {
461 /* pretend that certain key events that GTK does not allow
462 to be used as accelerators are actually something that
468 switch (ev->keyval) {
470 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_nabla, GdkModifierType(ev->state));
473 // some X and/or GDK implementations do Shift-Tab -> GDK_ISO_Left_Tab
475 case GDK_ISO_Left_Tab:
476 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_nabla, GdkModifierType(ev->state));
480 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
484 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
488 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
492 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
504 /* consider all relevant modifiers but not LOCK or SHIFT */
506 guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
508 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
510 /* no special handling or there are modifiers in effect: accelerate first */
512 #ifdef DEBUG_ACCELERATOR_HANDLING
514 cerr << "\tactivate, then propagate\n";
518 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
522 if (!gtk_window_activate_key (win, ev)) {
523 #ifdef DEBUG_ACCELERATOR_HANDLING
525 cerr << "\tnot accelerated, now propagate\n";
528 return gtk_window_propagate_key_event (win, ev);
530 #ifdef DEBUG_ACCELERATOR_HANDLING
532 cerr << "\taccelerated - done.\n";
539 /* no modifiers, propagate first */
541 #ifdef DEBUG_ACCELERATOR_HANDLING
543 cerr << "\tpropagate, then activate\n";
546 if (!gtk_window_propagate_key_event (win, ev)) {
547 #ifdef DEBUG_ACCELERATOR_HANDLING
549 cerr << "\tpropagation didn't handle, so activate\n";
553 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
557 return gtk_window_activate_key (win, ev);
559 #ifdef DEBUG_ACCELERATOR_HANDLING
561 cerr << "\thandled by propagate\n";
567 #ifdef DEBUG_ACCELERATOR_HANDLING
569 cerr << "\tnot handled\n";
575 Glib::RefPtr<Gdk::Pixbuf>
576 get_xpm (std::string name)
578 SearchPath spath(ARDOUR::ardour_search_path());
579 spath += ARDOUR::system_data_search_path();
581 spath.add_subdirectory_to_paths("pixmaps");
583 sys::path data_file_path;
585 if(!find_file_in_search_path (spath, name, data_file_path)) {
586 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
589 return Gdk::Pixbuf::create_from_file (data_file_path.to_string());
592 Glib::RefPtr<Gdk::Pixbuf>
593 get_icon (const char* cname)
598 SearchPath spath(ARDOUR::ardour_search_path());
599 spath += ARDOUR::system_data_search_path();
601 spath.add_subdirectory_to_paths("icons");
603 sys::path data_file_path;
605 if (!find_file_in_search_path (spath, name, data_file_path)) {
606 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
609 Glib::RefPtr<Gdk::Pixbuf> img;
611 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
613 catch (const Gdk::PixbufError &e)
615 cerr << "Caught PixbufError: " << e.what() << endl;
619 g_message("Caught ... ");
626 longest (vector<string>& strings)
628 if (strings.empty()) {
632 vector<string>::iterator longest = strings.begin();
633 string::size_type longest_length = (*longest).length();
635 vector<string>::iterator i = longest;
638 while (i != strings.end()) {
640 string::size_type len = (*i).length();
642 if (len > longest_length) {
644 longest_length = len;
654 key_is_legal_for_numeric_entry (guint keyval)
672 case GDK_KP_Subtract: