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.
25 #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"
48 short_version (string orig, string::size_type target_length)
50 /* this tries to create a recognizable abbreviation
51 of "orig" by removing characters until we meet
52 a certain target length.
54 note that we deliberately leave digits in the result
59 string::size_type pos;
61 /* remove white-space and punctuation, starting at end */
63 while (orig.length() > target_length) {
64 if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
67 orig.replace (pos, 1, "");
70 /* remove lower-case vowels, starting at end */
72 while (orig.length() > target_length) {
73 if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
76 orig.replace (pos, 1, "");
79 /* remove upper-case vowels, starting at end */
81 while (orig.length() > target_length) {
82 if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
85 orig.replace (pos, 1, "");
88 /* remove lower-case consonants, starting at end */
90 while (orig.length() > target_length) {
91 if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
94 orig.replace (pos, 1, "");
97 /* remove upper-case consonants, starting at end */
99 while (orig.length() > target_length) {
100 if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
103 orig.replace (pos, 1, "");
106 /* whatever the length is now, use it */
112 fit_to_pixels (const string & str, int pixel_width, const string & font)
117 Pango::FontDescription fontdesc (font);
119 int namelen = str.length();
120 char cstr[namelen+1];
121 strcpy (cstr, str.c_str());
124 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (cstr);
126 layout->set_font_description (fontdesc);
127 layout->get_pixel_size (width, height);
129 if (width < (pixel_width)) {
134 cstr[namelen] = '\0';
142 atoi (const string& s)
144 return atoi (s.c_str());
148 atof (const string& s)
150 return atof (s.c_str());
154 internationalize (const char **array)
158 for (uint32_t i = 0; array[i]; ++i) {
159 v.push_back (_(array[i]));
166 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
172 /* xpm2rgb copied from nixieclock, which bore the legend:
174 nixieclock - a nixie desktop timepiece
175 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
177 and was released under the GPL.
181 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
183 static long vals[256], val;
184 uint32_t t, x, y, colors, cpp;
186 unsigned char *savergb, *rgb;
190 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
191 error << string_compose (_("bad XPM header %1"), xpm[0])
196 savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
198 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
199 for (t = 0; t < colors; ++t) {
200 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
204 // COLORMAP -> RGB CONVERSION
205 // Get low 3 bytes from vals[]
209 for (y = h-1; y > 0; --y) {
211 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
212 val = vals[(int)*p++];
213 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
214 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
215 *(rgb+0) = val & 0xff; // 0:R
223 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
225 static long vals[256], val;
226 uint32_t t, x, y, colors, cpp;
228 unsigned char *savergb, *rgb;
233 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
234 error << string_compose (_("bad XPM header %1"), xpm[0])
239 savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
241 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
243 if (strstr (xpm[1], "None")) {
244 sscanf (xpm[1], "%c", &transparent);
251 for (; t < colors; ++t) {
252 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
256 // COLORMAP -> RGB CONVERSION
257 // Get low 3 bytes from vals[]
261 for (y = h-1; y > 0; --y) {
265 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
267 if (transparent && (*p++ == transparent)) {
275 *(rgb+3) = alpha; // 3: alpha
276 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
277 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
278 *(rgb+0) = val & 0xff; // 0:R
285 ArdourCanvas::Points*
286 get_canvas_points (string who, uint32_t npoints)
288 // cerr << who << ": wants " << npoints << " canvas points" << endl;
289 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
290 if (npoints > (uint32_t) gdk_screen_width() + 4) {
294 return new ArdourCanvas::Points (npoints);
298 int_from_hex (char hic, char loc)
300 int hi; /* hi byte */
301 int lo; /* low byte */
305 if( ('0'<=hi) && (hi<='9') ) {
307 } else if( ('a'<= hi) && (hi<= 'f') ) {
309 } else if( ('A'<=hi) && (hi<='F') ) {
315 if( ('0'<=lo) && (lo<='9') ) {
317 } else if( ('a'<=lo) && (lo<='f') ) {
319 } else if( ('A'<=lo) && (lo<='F') ) {
323 return lo + (16 * hi);
327 url_decode (string& url)
329 string::iterator last;
330 string::iterator next;
332 for (string::iterator i = url.begin(); i != url.end(); ++i) {
338 if (url.length() <= 3) {
344 --last; /* points at last char */
345 --last; /* points at last char - 1 */
347 for (string::iterator i = url.begin(); i != last; ) {
358 if (isxdigit (*i) && isxdigit (*next)) {
359 /* replace first digit with char */
360 *i = int_from_hex (*i,*next);
361 ++i; /* points at 2nd of 2 digits */
370 Pango::FontDescription
371 get_font_for_style (string widgetname)
373 Gtk::Window window (WINDOW_TOPLEVEL);
375 Glib::RefPtr<Style> style;
378 foobar.set_name (widgetname);
379 foobar.ensure_style();
381 style = foobar.get_style ();
382 return style->get_font();
386 pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
388 if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
392 if (Keyboard::is_delete_event (ev)) {
397 pos = pane->get_position ();
399 if (dynamic_cast<VPaned*>(pane)) {
400 cmp = pane->get_height();
402 cmp = pane->get_width();
405 /* we have to use approximations here because we can't predict the
406 exact position or sizes of the pane (themes, etc)
409 if (pos < 10 || abs (pos - cmp) < 10) {
411 /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
413 pane->set_position ((intptr_t) pane->get_data ("rpos"));
417 int collapse_direction;
419 /* store the current position */
421 pane->set_data ("rpos", (gpointer) pos);
423 /* collapse to show the relevant child in full */
425 collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
427 if (collapse_direction) {
428 pane->set_position (1);
430 if (dynamic_cast<VPaned*>(pane)) {
431 pane->set_position (pane->get_height());
433 pane->set_position (pane->get_width());
444 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
446 /* In GTK+2, styles aren't set up correctly if the widget is not
447 attached to a toplevel window that has a screen pointer.
450 static Gtk::Window* window = 0;
453 window = new Window (WINDOW_TOPLEVEL);
460 foo.set_name (style);
463 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
467 r = waverc->fg[state].red / 257;
468 g = waverc->fg[state].green / 257;
469 b = waverc->fg[state].blue / 257;
470 /* what a hack ... "a" is for "active" */
471 if (state == Gtk::STATE_NORMAL && rgba) {
472 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
474 } else if (attr == "bg") {
476 r = waverc->bg[state].red / 257;
477 g = waverc->bg[state].green / 257;
478 b = waverc->bg[state].blue / 257;
479 } else if (attr == "base") {
480 r = waverc->base[state].red / 257;
481 g = waverc->base[state].green / 257;
482 b = waverc->base[state].blue / 257;
483 } else if (attr == "text") {
484 r = waverc->text[state].red / 257;
485 g = waverc->text[state].green / 257;
486 b = waverc->text[state].blue / 257;
489 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
494 if (state == Gtk::STATE_NORMAL && rgba) {
495 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
497 return (uint32_t) RGB_TO_UINT(r,g,b);
502 canvas_item_visible (ArdourCanvas::Item* item)
504 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
508 set_color (Gdk::Color& c, int rgb)
510 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
514 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
516 GtkWindow* win = window.gobj();
517 GtkWidget* focus = gtk_window_get_focus (win);
518 bool special_handling_of_unmodified_accelerators = false;
521 if (GTK_IS_ENTRY(focus)) {
522 special_handling_of_unmodified_accelerators = true;
526 /* This exists to allow us to override the way GTK handles
527 key events. The normal sequence is:
529 a) event is delivered to a GtkWindow
530 b) accelerators/mnemonics are activated
531 c) if (b) didn't handle the event, propagate to
532 the focus widget and/or focus chain
534 The problem with this is that if the accelerators include
535 keys without modifiers, such as the space bar or the
536 letter "e", then pressing the key while typing into
537 a text entry widget results in the accelerator being
538 activated, instead of the desired letter appearing
541 There is no good way of fixing this, but this
542 represents a compromise. The idea is that
543 key events involving modifiers (not Shift)
544 get routed into the activation pathway first, then
545 get propagated to the focus widget if necessary.
547 If the key event doesn't involve modifiers,
548 we deliver to the focus widget first, thus allowing
549 it to get "normal text" without interference
552 Of course, this can also be problematic: if there
553 is a widget with focus, then it will swallow
554 all "normal text" accelerators.
557 if (!special_handling_of_unmodified_accelerators ||
558 ev->state & (Gdk::MOD1_MASK|
566 /* no special handling or modifiers in effect: accelerate first */
568 if (!gtk_window_activate_key (win, ev)) {
569 return gtk_window_propagate_key_event (win, ev);
575 /* no modifiers, propagate first */
577 if (!gtk_window_propagate_key_event (win, ev)) {
578 return gtk_window_activate_key (win, ev);
585 Glib::RefPtr<Gdk::Pixbuf>
586 get_xpm (std::string name)
588 if (!xpm_map[name]) {
589 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
592 return (xpm_map[name]);