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>
25 #include <gtkmm/window.h>
26 #include <gtkmm/combo.h>
27 #include <gtkmm/label.h>
28 #include <gtkmm/paned.h>
29 #include <gtk/gtkpaned.h>
31 #include <pbd/file_utils.h>
33 #include <gtkmm2ext/utils.h>
35 #include <ardour/filesystem_paths.h>
37 #include "ardour_ui.h"
41 #include "rgb_macros.h"
42 #include "canvas_impl.h"
51 pixel_width (const ustring& str, Pango::FontDescription& font)
54 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
56 layout->set_font_description (font);
57 layout->set_text (str);
60 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
65 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
68 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
69 ustring::size_type shorter_by = 0;
72 layout->set_font_description (font);
77 ustring::iterator last = ustr.end();
78 --last; /* now points at final entry */
82 while (!ustr.empty()) {
84 layout->set_text (txt);
87 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
89 if (width < pixel_width) {
97 if (with_ellipses && shorter_by > 3) {
109 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
115 /* xpm2rgb copied from nixieclock, which bore the legend:
117 nixieclock - a nixie desktop timepiece
118 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
120 and was released under the GPL.
124 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
126 static long vals[256], val;
127 uint32_t t, x, y, colors, cpp;
129 unsigned char *savergb, *rgb;
133 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
134 error << string_compose (_("bad XPM header %1"), xpm[0])
139 savergb = rgb = (unsigned char*) malloc (h * w * 3);
141 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
142 for (t = 0; t < colors; ++t) {
143 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
147 // COLORMAP -> RGB CONVERSION
148 // Get low 3 bytes from vals[]
152 for (y = h-1; y > 0; --y) {
154 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
155 val = vals[(int)*p++];
156 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
157 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
158 *(rgb+0) = val & 0xff; // 0:R
166 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
168 static long vals[256], val;
169 uint32_t t, x, y, colors, cpp;
171 unsigned char *savergb, *rgb;
176 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
177 error << string_compose (_("bad XPM header %1"), xpm[0])
182 savergb = rgb = (unsigned char*) malloc (h * w * 4);
184 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
186 if (strstr (xpm[1], "None")) {
187 sscanf (xpm[1], "%c", &transparent);
194 for (; t < colors; ++t) {
195 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
199 // COLORMAP -> RGB CONVERSION
200 // Get low 3 bytes from vals[]
204 for (y = h-1; y > 0; --y) {
208 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
210 if (transparent && (*p++ == transparent)) {
218 *(rgb+3) = alpha; // 3: alpha
219 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
220 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
221 *(rgb+0) = val & 0xff; // 0:R
228 ArdourCanvas::Points*
229 get_canvas_points (string who, uint32_t npoints)
231 // cerr << who << ": wants " << npoints << " canvas points" << endl;
232 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
233 if (npoints > (uint32_t) gdk_screen_width() + 4) {
237 return new ArdourCanvas::Points (npoints);
240 Pango::FontDescription*
241 get_font_for_style (string widgetname)
243 Gtk::Window window (WINDOW_TOPLEVEL);
245 Glib::RefPtr<Gtk::Style> style;
248 foobar.set_name (widgetname);
249 foobar.ensure_style();
251 style = foobar.get_style ();
253 Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
255 PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
259 /* layout inherited its font description from a PangoContext */
261 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
262 pfd = pango_context_get_font_description (ctxt);
263 return new Pango::FontDescription (pfd, true); /* make a copy */
266 return new Pango::FontDescription (pfd, true); /* make a copy */
270 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
272 /* In GTK+2, styles aren't set up correctly if the widget is not
273 attached to a toplevel window that has a screen pointer.
276 static Gtk::Window* window = 0;
279 window = new Window (WINDOW_TOPLEVEL);
286 foo.set_name (style);
289 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
293 r = waverc->fg[state].red / 257;
294 g = waverc->fg[state].green / 257;
295 b = waverc->fg[state].blue / 257;
297 /* what a hack ... "a" is for "active" */
298 if (state == Gtk::STATE_NORMAL && rgba) {
299 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
301 } else if (attr == "bg") {
303 r = waverc->bg[state].red / 257;
304 g = waverc->bg[state].green / 257;
305 b = waverc->bg[state].blue / 257;
306 } else if (attr == "base") {
307 r = waverc->base[state].red / 257;
308 g = waverc->base[state].green / 257;
309 b = waverc->base[state].blue / 257;
310 } else if (attr == "text") {
311 r = waverc->text[state].red / 257;
312 g = waverc->text[state].green / 257;
313 b = waverc->text[state].blue / 257;
316 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
321 if (state == Gtk::STATE_NORMAL && rgba) {
322 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
324 return (uint32_t) RGB_TO_UINT(r,g,b);
329 canvas_item_visible (ArdourCanvas::Item* item)
331 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
335 set_color (Gdk::Color& c, int rgb)
337 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
342 gboolean gdk_quartz_possibly_forward (GdkEvent*);
347 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
349 GtkWindow* win = window.gobj();
350 GtkWidget* focus = gtk_window_get_focus (win);
351 bool special_handling_of_unmodified_accelerators = false;
353 #undef DEBUG_ACCELERATOR_HANDLING
354 #ifdef DEBUG_ACCELERATOR_HANDLING
355 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
358 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
359 special_handling_of_unmodified_accelerators = true;
363 #ifdef DEBUG_ACCELERATOR_HANDLING
365 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
366 << special_handling_of_unmodified_accelerators
371 /* This exists to allow us to override the way GTK handles
372 key events. The normal sequence is:
374 a) event is delivered to a GtkWindow
375 b) accelerators/mnemonics are activated
376 c) if (b) didn't handle the event, propagate to
377 the focus widget and/or focus chain
379 The problem with this is that if the accelerators include
380 keys without modifiers, such as the space bar or the
381 letter "e", then pressing the key while typing into
382 a text entry widget results in the accelerator being
383 activated, instead of the desired letter appearing
386 There is no good way of fixing this, but this
387 represents a compromise. The idea is that
388 key events involving modifiers (not Shift)
389 get routed into the activation pathway first, then
390 get propagated to the focus widget if necessary.
392 If the key event doesn't involve modifiers,
393 we deliver to the focus widget first, thus allowing
394 it to get "normal text" without interference
397 Of course, this can also be problematic: if there
398 is a widget with focus, then it will swallow
399 all "normal text" accelerators.
403 if (!special_handling_of_unmodified_accelerators) {
405 /* pretend that certain key events that GTK does not allow
406 to be used as accelerators are actually something that
412 switch (ev->keyval) {
414 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
418 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
422 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
426 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
438 if (!special_handling_of_unmodified_accelerators ||
439 ev->state & (Gdk::MOD1_MASK|
443 Gdk::CONTROL_MASK)) {
445 /* no special handling or modifiers in effect: accelerate first */
447 #ifdef DEBUG_ACCELERATOR_HANDLING
449 cerr << "\tactivate, then propagate\n";
453 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
457 if (!gtk_window_activate_key (win, ev)) {
458 return gtk_window_propagate_key_event (win, ev);
460 #ifdef DEBUG_ACCELERATOR_HANDLING
462 cerr << "\tnot handled\n";
469 /* no modifiers, propagate first */
471 #ifdef DEBUG_ACCELERATOR_HANDLING
473 cerr << "\tpropagate, then activate\n";
476 if (!gtk_window_propagate_key_event (win, ev)) {
477 #ifdef DEBUG_ACCELERATOR_HANDLING
479 cerr << "\tpropagation didn't handle, so activate\n";
483 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
487 return gtk_window_activate_key (win, ev);
489 #ifdef DEBUG_ACCELERATOR_HANDLING
491 cerr << "\thandled by propagate\n";
497 #ifdef DEBUG_ACCELERATOR_HANDLING
499 cerr << "\tnot handled\n";
505 Glib::RefPtr<Gdk::Pixbuf>
506 get_xpm (std::string name)
508 SearchPath spath(ARDOUR::ardour_search_path());
509 spath += ARDOUR::system_data_search_path();
511 spath.add_subdirectory_to_paths("pixmaps");
513 sys::path data_file_path;
515 if(!find_file_in_search_path (spath, name, data_file_path)) {
516 fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
519 return Gdk::Pixbuf::create_from_file (data_file_path.to_string());
522 Glib::RefPtr<Gdk::Pixbuf>
523 get_icon (const char* cname)
528 SearchPath spath(ARDOUR::ardour_search_path());
529 spath += ARDOUR::system_data_search_path();
531 spath.add_subdirectory_to_paths("icons");
533 sys::path data_file_path;
535 if(!find_file_in_search_path (spath, name, data_file_path)) {
536 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
539 return Gdk::Pixbuf::create_from_file (data_file_path.to_string());
543 longest (vector<string>& strings)
545 if (strings.empty()) {
549 vector<string>::iterator longest = strings.begin();
550 string::size_type longest_length = (*longest).length();
552 vector<string>::iterator i = longest;
555 while (i != strings.end()) {
557 string::size_type len = (*i).length();
559 if (len > longest_length) {
561 longest_length = len;
571 key_is_legal_for_numeric_entry (guint keyval)
589 case GDK_KP_Subtract: