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.
23 #include <libart_lgpl/art_misc.h>
24 #include <gtkmm/window.h>
25 #include <gtkmm/combo.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/paned.h>
28 #include <gtk/gtkpaned.h>
30 #include <gtkmm2ext/utils.h>
31 #include <ardour/ardour.h>
33 #include "ardour_ui.h"
37 #include "rgb_macros.h"
38 #include "canvas_impl.h"
46 short_version (string orig, string::size_type target_length)
48 /* this tries to create a recognizable abbreviation
49 of "orig" by removing characters until we meet
50 a certain target length.
52 note that we deliberately leave digits in the result
57 string::size_type pos;
59 /* remove white-space and punctuation, starting at end */
61 while (orig.length() > target_length) {
62 if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
65 orig.replace (pos, 1, "");
68 /* remove lower-case vowels, starting at end */
70 while (orig.length() > target_length) {
71 if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
74 orig.replace (pos, 1, "");
77 /* remove upper-case vowels, starting at end */
79 while (orig.length() > target_length) {
80 if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
83 orig.replace (pos, 1, "");
86 /* remove lower-case consonants, starting at end */
88 while (orig.length() > target_length) {
89 if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
92 orig.replace (pos, 1, "");
95 /* remove upper-case consonants, starting at end */
97 while (orig.length() > target_length) {
98 if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
101 orig.replace (pos, 1, "");
104 /* whatever the length is now, use it */
110 fit_to_pixels (const string & str, int pixel_width, const string & font)
115 Pango::FontDescription fontdesc (font);
117 int namelen = str.length();
118 char cstr[namelen+1];
119 strcpy (cstr, str.c_str());
122 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (cstr);
124 layout->set_font_description (fontdesc);
125 layout->get_pixel_size (width, height);
127 if (width < (pixel_width)) {
132 cstr[namelen] = '\0';
140 atoi (const string& s)
142 return atoi (s.c_str());
146 atof (const string& s)
148 return atof (s.c_str());
152 internationalize (const char **array)
156 for (uint32_t i = 0; array[i]; ++i) {
157 v.push_back (_(array[i]));
164 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
170 /* xpm2rgb copied from nixieclock, which bore the legend:
172 nixieclock - a nixie desktop timepiece
173 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
175 and was released under the GPL.
179 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
181 static long vals[256], val;
182 uint32_t t, x, y, colors, cpp;
184 unsigned char *savergb, *rgb;
188 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
189 error << string_compose (_("bad XPM header %1"), xpm[0])
194 savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
196 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
197 for (t = 0; t < colors; ++t) {
198 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
202 // COLORMAP -> RGB CONVERSION
203 // Get low 3 bytes from vals[]
207 for (y = h-1; y > 0; --y) {
209 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
210 val = vals[(int)*p++];
211 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
212 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
213 *(rgb+0) = val & 0xff; // 0:R
221 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
223 static long vals[256], val;
224 uint32_t t, x, y, colors, cpp;
226 unsigned char *savergb, *rgb;
231 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
232 error << string_compose (_("bad XPM header %1"), xpm[0])
237 savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
239 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
241 if (strstr (xpm[1], "None")) {
242 sscanf (xpm[1], "%c", &transparent);
249 for (; t < colors; ++t) {
250 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
254 // COLORMAP -> RGB CONVERSION
255 // Get low 3 bytes from vals[]
259 for (y = h-1; y > 0; --y) {
263 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
265 if (transparent && (*p++ == transparent)) {
273 *(rgb+3) = alpha; // 3: alpha
274 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
275 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
276 *(rgb+0) = val & 0xff; // 0:R
283 ArdourCanvas::Points*
284 get_canvas_points (string who, uint32_t npoints)
286 // cerr << who << ": wants " << npoints << " canvas points" << endl;
287 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
288 if (npoints > (uint32_t) gdk_screen_width() + 4) {
292 return new ArdourCanvas::Points (npoints);
296 int_from_hex (char hic, char loc)
298 int hi; /* hi byte */
299 int lo; /* low byte */
303 if( ('0'<=hi) && (hi<='9') ) {
305 } else if( ('a'<= hi) && (hi<= 'f') ) {
307 } else if( ('A'<=hi) && (hi<='F') ) {
313 if( ('0'<=lo) && (lo<='9') ) {
315 } else if( ('a'<=lo) && (lo<='f') ) {
317 } else if( ('A'<=lo) && (lo<='F') ) {
321 return lo + (16 * hi);
325 url_decode (string& url)
327 string::iterator last;
328 string::iterator next;
330 for (string::iterator i = url.begin(); i != url.end(); ++i) {
336 if (url.length() <= 3) {
342 --last; /* points at last char */
343 --last; /* points at last char - 1 */
345 for (string::iterator i = url.begin(); i != last; ) {
356 if (isxdigit (*i) && isxdigit (*next)) {
357 /* replace first digit with char */
358 *i = int_from_hex (*i,*next);
359 ++i; /* points at 2nd of 2 digits */
368 Pango::FontDescription
369 get_font_for_style (string widgetname)
371 Gtk::Window window (WINDOW_TOPLEVEL);
373 Glib::RefPtr<Style> style;
376 foobar.set_name (widgetname);
377 foobar.ensure_style();
379 style = foobar.get_style ();
380 return style->get_font();
384 pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
386 if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
390 if (Keyboard::is_delete_event (ev)) {
395 pos = pane->get_position ();
397 if (dynamic_cast<VPaned*>(pane)) {
398 cmp = pane->get_height();
400 cmp = pane->get_width();
403 /* we have to use approximations here because we can't predict the
404 exact position or sizes of the pane (themes, etc)
407 if (pos < 10 || abs (pos - cmp) < 10) {
409 /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
411 pane->set_position ((intptr_t) pane->get_data ("rpos"));
415 int collapse_direction;
417 /* store the current position */
419 pane->set_data ("rpos", (gpointer) pos);
421 /* collapse to show the relevant child in full */
423 collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
425 if (collapse_direction) {
426 pane->set_position (1);
428 if (dynamic_cast<VPaned*>(pane)) {
429 pane->set_position (pane->get_height());
431 pane->set_position (pane->get_width());
442 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
444 /* In GTK+2, styles aren't set up correctly if the widget is not
445 attached to a toplevel window that has a screen pointer.
448 static Gtk::Window* window = 0;
451 window = new Window (WINDOW_TOPLEVEL);
458 foo.set_name (style);
461 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
465 r = waverc->fg[state].red / 257;
466 g = waverc->fg[state].green / 257;
467 b = waverc->fg[state].blue / 257;
468 /* what a hack ... "a" is for "active" */
469 if (state == Gtk::STATE_NORMAL && rgba) {
470 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
472 } else if (attr == "bg") {
474 r = waverc->bg[state].red / 257;
475 g = waverc->bg[state].green / 257;
476 b = waverc->bg[state].blue / 257;
477 } else if (attr == "base") {
478 r = waverc->base[state].red / 257;
479 g = waverc->base[state].green / 257;
480 b = waverc->base[state].blue / 257;
481 } else if (attr == "text") {
482 r = waverc->text[state].red / 257;
483 g = waverc->text[state].green / 257;
484 b = waverc->text[state].blue / 257;
487 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
492 if (state == Gtk::STATE_NORMAL && rgba) {
493 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
495 return (uint32_t) RGB_TO_UINT(r,g,b);
500 canvas_item_visible (ArdourCanvas::Item* item)
502 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
506 set_color (Gdk::Color& c, int rgb)
508 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
512 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
514 GtkWindow* win = window.gobj();
516 /* This exists to allow us to override the way GTK handles
517 key events. The normal sequence is:
519 a) event is delivered to a GtkWindow
520 b) accelerators/mnemonics are activated
521 c) if (b) didn't handle the event, propagate to
522 the focus widget and/or focus chain
524 The problem with this is that if the accelerators include
525 keys without modifiers, such as the space bar or the
526 letter "e", then pressing the key while typing into
527 a text entry widget results in the accelerator being
528 activated, instead of the desired letter appearing
531 There is no good way of fixing this, but this
532 represents a compromise. The idea is that
533 key events involving modifiers (not Shift)
534 get routed into the activation pathway first, then
535 get propagated to the focus widget if necessary.
537 If the key event doesn't involve modifiers,
538 we deliver to the focus widget first, thus allowing
539 it to get "normal text" without interference
542 Of course, this can also be problematic: if there
543 is a widget with focus, then it will swallow
544 all "normal text" accelerators.
547 if (ev->state & ~Gdk::SHIFT_MASK) {
549 /* modifiers in effect, accelerate first */
550 if (!gtk_window_activate_key (win, ev)) {
551 return gtk_window_propagate_key_event (win, ev);
557 /* no modifiers, propagate first */
559 if (!gtk_window_propagate_key_event (win, ev)) {
560 return gtk_window_activate_key (win, ev);
567 Glib::RefPtr<Gdk::Pixbuf>
568 get_xpm (std::string name)
570 if (!xpm_map[name]) {
571 xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
574 return (xpm_map[name]);