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);
341 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
343 GtkWindow* win = window.gobj();
344 GtkWidget* focus = gtk_window_get_focus (win);
345 bool special_handling_of_unmodified_accelerators = false;
347 #undef DEBUG_ACCELERATOR_HANDLING
348 #ifdef DEBUG_ACCELERATOR_HANDLING
349 bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
353 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
354 special_handling_of_unmodified_accelerators = true;
358 #ifdef DEBUG_ACCELERATOR_HANDLING
360 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
361 << special_handling_of_unmodified_accelerators
366 /* This exists to allow us to override the way GTK handles
367 key events. The normal sequence is:
369 a) event is delivered to a GtkWindow
370 b) accelerators/mnemonics are activated
371 c) if (b) didn't handle the event, propagate to
372 the focus widget and/or focus chain
374 The problem with this is that if the accelerators include
375 keys without modifiers, such as the space bar or the
376 letter "e", then pressing the key while typing into
377 a text entry widget results in the accelerator being
378 activated, instead of the desired letter appearing
381 There is no good way of fixing this, but this
382 represents a compromise. The idea is that
383 key events involving modifiers (not Shift)
384 get routed into the activation pathway first, then
385 get propagated to the focus widget if necessary.
387 If the key event doesn't involve modifiers,
388 we deliver to the focus widget first, thus allowing
389 it to get "normal text" without interference
392 Of course, this can also be problematic: if there
393 is a widget with focus, then it will swallow
394 all "normal text" accelerators.
398 if (!special_handling_of_unmodified_accelerators) {
400 /* pretend that certain key events that GTK does not allow
401 to be used as accelerators are actually something that
407 switch (ev->keyval) {
409 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
413 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
417 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
421 ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
433 if (!special_handling_of_unmodified_accelerators ||
434 ev->state & (Gdk::MOD1_MASK|
438 Gdk::CONTROL_MASK)) {
440 /* no special handling or modifiers in effect: accelerate first */
442 #ifdef DEBUG_ACCELERATOR_HANDLING
444 cerr << "\tactivate, then propagate\n";
447 if (!gtk_window_activate_key (win, ev)) {
448 return gtk_window_propagate_key_event (win, ev);
450 #ifdef DEBUG_ACCELERATOR_HANDLING
452 cerr << "\tnot handled\n";
459 /* no modifiers, propagate first */
461 #ifdef DEBUG_ACCELERATOR_HANDLING
463 cerr << "\tpropagate, then activate\n";
466 if (!gtk_window_propagate_key_event (win, ev)) {
467 #ifdef DEBUG_ACCELERATOR_HANDLING
469 cerr << "\tpropagation didn't handle, so activate\n";
472 return gtk_window_activate_key (win, ev);
474 #ifdef DEBUG_ACCELERATOR_HANDLING
476 cerr << "\thandled by propagate\n";
482 #ifdef DEBUG_ACCELERATOR_HANDLING
484 cerr << "\tnot handled\n";
490 Glib::RefPtr<Gdk::Pixbuf>
491 get_xpm (std::string name)
493 if (!xpm_map[name]) {
495 SearchPath spath(ARDOUR::ardour_search_path());
496 spath += ARDOUR::system_data_search_path();
498 spath.add_subdirectory_to_paths("pixmaps");
500 sys::path data_file_path;
502 if(!find_file_in_search_path (spath, name, data_file_path)) {
503 fatal << string_compose (_("cannot find pixmap %1"), name) << endmsg;
506 xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
509 return (xpm_map[name]);
512 Glib::RefPtr<Gdk::Pixbuf>
513 get_icon (const char* cname)
518 SearchPath spath(ARDOUR::ardour_search_path());
519 spath += ARDOUR::system_data_search_path();
521 spath.add_subdirectory_to_paths("icons");
523 sys::path data_file_path;
525 if(!find_file_in_search_path (spath, name, data_file_path)) {
526 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
529 return Gdk::Pixbuf::create_from_file (data_file_path.to_string());
533 longest (vector<string>& strings)
535 if (strings.empty()) {
539 vector<string>::iterator longest = strings.begin();
540 string::size_type longest_length = (*longest).length();
542 vector<string>::iterator i = longest;
545 while (i != strings.end()) {
547 string::size_type len = (*i).length();
549 if (len > longest_length) {
551 longest_length = len;
561 key_is_legal_for_numeric_entry (guint keyval)
579 case GDK_KP_Subtract: