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 <gtkmm2ext/utils.h>
33 #include <ardour/ardour.h>
35 #include "ardour_ui.h"
39 #include "rgb_macros.h"
40 #include "canvas_impl.h"
49 pixel_width (const ustring& str, Pango::FontDescription& font)
52 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
54 layout->set_font_description (font);
55 layout->set_text (str);
58 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
63 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
66 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
67 ustring::size_type shorter_by = 0;
70 layout->set_font_description (font);
75 ustring::iterator last = ustr.end();
76 --last; /* now points at final entry */
80 while (!ustr.empty()) {
82 layout->set_text (txt);
85 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
87 if (width < pixel_width) {
95 if (with_ellipses && shorter_by > 3) {
107 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
113 /* xpm2rgb copied from nixieclock, which bore the legend:
115 nixieclock - a nixie desktop timepiece
116 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
118 and was released under the GPL.
122 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
124 static long vals[256], val;
125 uint32_t t, x, y, colors, cpp;
127 unsigned char *savergb, *rgb;
131 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
132 error << string_compose (_("bad XPM header %1"), xpm[0])
137 savergb = rgb = (unsigned char*) malloc (h * w * 3);
139 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
140 for (t = 0; t < colors; ++t) {
141 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
145 // COLORMAP -> RGB CONVERSION
146 // Get low 3 bytes from vals[]
150 for (y = h-1; y > 0; --y) {
152 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
153 val = vals[(int)*p++];
154 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
155 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
156 *(rgb+0) = val & 0xff; // 0:R
164 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
166 static long vals[256], val;
167 uint32_t t, x, y, colors, cpp;
169 unsigned char *savergb, *rgb;
174 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
175 error << string_compose (_("bad XPM header %1"), xpm[0])
180 savergb = rgb = (unsigned char*) malloc (h * w * 4);
182 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
184 if (strstr (xpm[1], "None")) {
185 sscanf (xpm[1], "%c", &transparent);
192 for (; t < colors; ++t) {
193 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
197 // COLORMAP -> RGB CONVERSION
198 // Get low 3 bytes from vals[]
202 for (y = h-1; y > 0; --y) {
206 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
208 if (transparent && (*p++ == transparent)) {
216 *(rgb+3) = alpha; // 3: alpha
217 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
218 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
219 *(rgb+0) = val & 0xff; // 0:R
226 ArdourCanvas::Points*
227 get_canvas_points (string who, uint32_t npoints)
229 // cerr << who << ": wants " << npoints << " canvas points" << endl;
230 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
231 if (npoints > (uint32_t) gdk_screen_width() + 4) {
235 return new ArdourCanvas::Points (npoints);
238 Pango::FontDescription*
239 get_font_for_style (string widgetname)
241 Gtk::Window window (WINDOW_TOPLEVEL);
243 Glib::RefPtr<Gtk::Style> style;
246 foobar.set_name (widgetname);
247 foobar.ensure_style();
249 style = foobar.get_style ();
251 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
253 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
257 /* layout inherited its font description from a PangoContext */
259 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
260 pfd = pango_context_get_font_description (ctxt);
261 return new Pango::FontDescription (pfd, true); /* make a copy */
264 return new Pango::FontDescription (pfd, true); /* make a copy */
268 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
270 /* In GTK+2, styles aren't set up correctly if the widget is not
271 attached to a toplevel window that has a screen pointer.
274 static Gtk::Window* window = 0;
277 window = new Window (WINDOW_TOPLEVEL);
284 foo.set_name (style);
287 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
291 r = waverc->fg[state].red / 257;
292 g = waverc->fg[state].green / 257;
293 b = waverc->fg[state].blue / 257;
295 /* what a hack ... "a" is for "active" */
296 if (state == Gtk::STATE_NORMAL && rgba) {
297 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
299 } else if (attr == "bg") {
301 r = waverc->bg[state].red / 257;
302 g = waverc->bg[state].green / 257;
303 b = waverc->bg[state].blue / 257;
304 } else if (attr == "base") {
305 r = waverc->base[state].red / 257;
306 g = waverc->base[state].green / 257;
307 b = waverc->base[state].blue / 257;
308 } else if (attr == "text") {
309 r = waverc->text[state].red / 257;
310 g = waverc->text[state].green / 257;
311 b = waverc->text[state].blue / 257;
314 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
319 if (state == Gtk::STATE_NORMAL && rgba) {
320 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
322 return (uint32_t) RGB_TO_UINT(r,g,b);
328 color_from_style (string widget_style_name, int state, string attr)
332 style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
333 widget_style_name.c_str(),
337 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
338 return Gdk::Color ("red");
341 cerr << "got style for " << widget_style_name << endl;
344 return Gdk::Color (&style->fg[state]);
348 cerr << "returning color from bg\n";
349 return Gdk::Color (&style->bg[state]);
352 if (attr == "light") {
353 return Gdk::Color (&style->light[state]);
356 if (attr == "dark") {
357 return Gdk::Color (&style->dark[state]);
361 return Gdk::Color (&style->mid[state]);
364 if (attr == "text") {
365 return Gdk::Color (&style->text[state]);
368 if (attr == "base") {
369 return Gdk::Color (&style->base[state]);
372 if (attr == "text_aa") {
373 return Gdk::Color (&style->text_aa[state]);
376 error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
377 return Gdk::Color ("red");
382 canvas_item_visible (ArdourCanvas::Item* item)
384 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
388 set_color (Gdk::Color& c, int rgb)
390 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
395 gboolean gdk_quartz_possibly_forward (GdkEvent*);
400 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
402 GtkWindow* win = window.gobj();
403 GtkWidget* focus = gtk_window_get_focus (win);
404 bool special_handling_of_unmodified_accelerators = false;
406 #undef DEBUG_ACCELERATOR_HANDLING
407 #ifdef DEBUG_ACCELERATOR_HANDLING
408 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
411 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
412 special_handling_of_unmodified_accelerators = true;
416 #ifdef DEBUG_ACCELERATOR_HANDLING
418 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
419 << special_handling_of_unmodified_accelerators
424 /* This exists to allow us to override the way GTK handles
425 key events. The normal sequence is:
427 a) event is delivered to a GtkWindow
428 b) accelerators/mnemonics are activated
429 c) if (b) didn't handle the event, propagate to
430 the focus widget and/or focus chain
432 The problem with this is that if the accelerators include
433 keys without modifiers, such as the space bar or the
434 letter "e", then pressing the key while typing into
435 a text entry widget results in the accelerator being
436 activated, instead of the desired letter appearing
439 There is no good way of fixing this, but this
440 represents a compromise. The idea is that
441 key events involving modifiers (not Shift)
442 get routed into the activation pathway first, then
443 get propagated to the focus widget if necessary.
445 If the key event doesn't involve modifiers,
446 we deliver to the focus widget first, thus allowing
447 it to get "normal text" without interference
450 Of course, this can also be problematic: if there
451 is a widget with focus, then it will swallow
452 all "normal text" accelerators.
456 if (!special_handling_of_unmodified_accelerators) {
458 /* pretend that certain key events that GTK does not allow
459 to be used as accelerators are actually something that
465 switch (ev->keyval) {
467 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
471 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
475 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
479 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
491 if (!special_handling_of_unmodified_accelerators ||
492 ev->state & (Gdk::MOD1_MASK|
496 Gdk::CONTROL_MASK)) {
498 /* no special handling or modifiers in effect: accelerate first */
500 #ifdef DEBUG_ACCELERATOR_HANDLING
502 cerr << "\tactivate, then propagate\n";
506 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
510 if (!gtk_window_activate_key (win, ev)) {
511 return gtk_window_propagate_key_event (win, ev);
513 #ifdef DEBUG_ACCELERATOR_HANDLING
515 cerr << "\tnot handled\n";
522 /* no modifiers, propagate first */
524 #ifdef DEBUG_ACCELERATOR_HANDLING
526 cerr << "\tpropagate, then activate\n";
529 if (!gtk_window_propagate_key_event (win, ev)) {
530 #ifdef DEBUG_ACCELERATOR_HANDLING
532 cerr << "\tpropagation didn't handle, so activate\n";
536 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
540 return gtk_window_activate_key (win, ev);
542 #ifdef DEBUG_ACCELERATOR_HANDLING
544 cerr << "\thandled by propagate\n";
550 #ifdef DEBUG_ACCELERATOR_HANDLING
552 cerr << "\tnot handled\n";
558 Glib::RefPtr<Gdk::Pixbuf>
559 get_xpm (std::string name)
561 if (!xpm_map[name]) {
562 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
565 return (xpm_map[name]);
568 Glib::RefPtr<Gdk::Pixbuf>
569 get_icon (const char* cname)
574 string path = ARDOUR::find_data_file (name, "icons");
577 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
581 return Gdk::Pixbuf::create_from_file (path);
585 longest (vector<string>& strings)
587 if (strings.empty()) {
591 vector<string>::iterator longest = strings.begin();
592 string::size_type longest_length = (*longest).length();
594 vector<string>::iterator i = longest;
597 while (i != strings.end()) {
599 string::size_type len = (*i).length();
601 if (len > longest_length) {
603 longest_length = len;
613 key_is_legal_for_numeric_entry (guint keyval)
631 case GDK_KP_Subtract: