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>
32 #include "ardour_ui.h"
36 #include "rgb_macros.h"
37 #include "canvas_impl.h"
45 short_version (string orig, string::size_type target_length)
47 /* this tries to create a recognizable abbreviation
48 of "orig" by removing characters until we meet
49 a certain target length.
51 note that we deliberately leave digits in the result
56 string::size_type pos;
58 /* remove white-space and punctuation, starting at end */
60 while (orig.length() > target_length) {
61 if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
64 orig.replace (pos, 1, "");
67 /* remove lower-case vowels, starting at end */
69 while (orig.length() > target_length) {
70 if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
73 orig.replace (pos, 1, "");
76 /* remove upper-case vowels, starting at end */
78 while (orig.length() > target_length) {
79 if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
82 orig.replace (pos, 1, "");
85 /* remove lower-case consonants, starting at end */
87 while (orig.length() > target_length) {
88 if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
91 orig.replace (pos, 1, "");
94 /* remove upper-case consonants, starting at end */
96 while (orig.length() > target_length) {
97 if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
100 orig.replace (pos, 1, "");
103 /* whatever the length is now, use it */
109 fit_to_pixels (const string & str, int pixel_width, const string & font)
114 Pango::FontDescription fontdesc (font);
116 int namelen = str.length();
117 char cstr[namelen+1];
118 strcpy (cstr, str.c_str());
121 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (cstr);
123 layout->set_font_description (fontdesc);
124 layout->get_pixel_size (width, height);
126 if (width < (pixel_width)) {
131 cstr[namelen] = '\0';
139 atoi (const string& s)
141 return atoi (s.c_str());
145 atof (const string& s)
147 return atof (s.c_str());
151 internationalize (const char **array)
155 for (uint32_t i = 0; array[i]; ++i) {
156 v.push_back (_(array[i]));
163 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
169 /* xpm2rgb copied from nixieclock, which bore the legend:
171 nixieclock - a nixie desktop timepiece
172 Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
174 and was released under the GPL.
178 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
180 static long vals[256], val;
181 uint32_t t, x, y, colors, cpp;
183 unsigned char *savergb, *rgb;
187 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
188 error << string_compose (_("bad XPM header %1"), xpm[0])
193 savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
195 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
196 for (t = 0; t < colors; ++t) {
197 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
201 // COLORMAP -> RGB CONVERSION
202 // Get low 3 bytes from vals[]
206 for (y = h-1; y > 0; --y) {
208 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
209 val = vals[(int)*p++];
210 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
211 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
212 *(rgb+0) = val & 0xff; // 0:R
220 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
222 static long vals[256], val;
223 uint32_t t, x, y, colors, cpp;
225 unsigned char *savergb, *rgb;
230 if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
231 error << string_compose (_("bad XPM header %1"), xpm[0])
236 savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
238 // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
240 if (strstr (xpm[1], "None")) {
241 sscanf (xpm[1], "%c", &transparent);
248 for (; t < colors; ++t) {
249 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
253 // COLORMAP -> RGB CONVERSION
254 // Get low 3 bytes from vals[]
258 for (y = h-1; y > 0; --y) {
262 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
264 if (transparent && (*p++ == transparent)) {
272 *(rgb+3) = alpha; // 3: alpha
273 *(rgb+2) = val & 0xff; val >>= 8; // 2:B
274 *(rgb+1) = val & 0xff; val >>= 8; // 1:G
275 *(rgb+0) = val & 0xff; // 0:R
282 ArdourCanvas::Points*
283 get_canvas_points (string who, uint32_t npoints)
285 // cerr << who << ": wants " << npoints << " canvas points" << endl;
286 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
287 if (npoints > (uint32_t) gdk_screen_width() + 4) {
291 return new ArdourCanvas::Points (npoints);
295 int_from_hex (char hic, char loc)
297 int hi; /* hi byte */
298 int lo; /* low byte */
302 if( ('0'<=hi) && (hi<='9') ) {
304 } else if( ('a'<= hi) && (hi<= 'f') ) {
306 } else if( ('A'<=hi) && (hi<='F') ) {
312 if( ('0'<=lo) && (lo<='9') ) {
314 } else if( ('a'<=lo) && (lo<='f') ) {
316 } else if( ('A'<=lo) && (lo<='F') ) {
320 return lo + (16 * hi);
324 url_decode (string& url)
326 string::iterator last;
327 string::iterator next;
329 for (string::iterator i = url.begin(); i != url.end(); ++i) {
335 if (url.length() <= 3) {
341 --last; /* points at last char */
342 --last; /* points at last char - 1 */
344 for (string::iterator i = url.begin(); i != last; ) {
355 if (isxdigit (*i) && isxdigit (*next)) {
356 /* replace first digit with char */
357 *i = int_from_hex (*i,*next);
358 ++i; /* points at 2nd of 2 digits */
367 Pango::FontDescription
368 get_font_for_style (string widgetname)
370 Gtk::Window window (WINDOW_TOPLEVEL);
372 Glib::RefPtr<Style> style;
375 foobar.set_name (widgetname);
376 foobar.ensure_style();
378 style = foobar.get_style ();
379 return style->get_font();
383 pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
385 if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
389 if (Keyboard::is_delete_event (ev)) {
394 pos = pane->get_position ();
396 if (dynamic_cast<VPaned*>(pane)) {
397 cmp = pane->get_height();
399 cmp = pane->get_width();
402 /* we have to use approximations here because we can't predict the
403 exact position or sizes of the pane (themes, etc)
406 if (pos < 10 || abs (pos - cmp) < 10) {
408 /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
410 pane->set_position ((intptr_t) pane->get_data ("rpos"));
414 int collapse_direction;
416 /* store the current position */
418 pane->set_data ("rpos", (gpointer) pos);
420 /* collapse to show the relevant child in full */
422 collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
424 if (collapse_direction) {
425 pane->set_position (1);
427 if (dynamic_cast<VPaned*>(pane)) {
428 pane->set_position (pane->get_height());
430 pane->set_position (pane->get_width());
441 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
443 /* In GTK+2, styles aren't set up correctly if the widget is not
444 attached to a toplevel window that has a screen pointer.
447 static Gtk::Window* window = 0;
450 window = new Window (WINDOW_TOPLEVEL);
457 foo.set_name (style);
460 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
464 r = waverc->fg[state].red / 257;
465 g = waverc->fg[state].green / 257;
466 b = waverc->fg[state].blue / 257;
467 /* what a hack ... "a" is for "active" */
468 if (state == Gtk::STATE_NORMAL && rgba) {
469 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
471 } else if (attr == "bg") {
473 r = waverc->bg[state].red / 257;
474 g = waverc->bg[state].green / 257;
475 b = waverc->bg[state].blue / 257;
476 } else if (attr == "base") {
477 r = waverc->base[state].red / 257;
478 g = waverc->base[state].green / 257;
479 b = waverc->base[state].blue / 257;
480 } else if (attr == "text") {
481 r = waverc->text[state].red / 257;
482 g = waverc->text[state].green / 257;
483 b = waverc->text[state].blue / 257;
486 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
491 if (state == Gtk::STATE_NORMAL && rgba) {
492 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
494 return (uint32_t) RGB_TO_UINT(r,g,b);
499 canvas_item_visible (ArdourCanvas::Item* item)
501 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
505 set_color (Gdk::Color& c, int rgb)
507 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
511 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
513 GtkWindow* win = window.gobj();
515 /* This exists to allow us to override the way GTK handles
516 key events. The normal sequence is:
518 a) event is delivered to a GtkWindow
519 b) accelerators/mnemonics are activated
520 c) if (b) didn't handle the event, propagate to
521 the focus widget and/or focus chain
523 The problem with this is that if the accelerators include
524 keys without modifiers, such as the space bar or the
525 letter "e", then pressing the key while typing into
526 a text entry widget results in the accelerator being
527 activated, instead of the desired letter appearing
530 There is no good way of fixing this, but this
531 represents a compromise. The idea is that
532 key events involving modifiers (not Shift)
533 get routed into the activation pathway first, then
534 get propagated to the focus widget if necessary.
536 If the key event doesn't involve modifiers,
537 we deliver to the focus widget first, thus allowing
538 it to get "normal text" without interference
541 Of course, this can also be problematic: if there
542 is a widget with focus, then it will swallow
543 all "normal text" accelerators.
546 if (ev->state & ~Gdk::SHIFT_MASK) {
547 /* modifiers in effect, accelerate first */
548 if (!gtk_window_activate_key (win, ev)) {
549 return gtk_window_propagate_key_event (win, ev);
555 /* no modifiers, propagate first */
557 if (!gtk_window_propagate_key_event (win, ev)) {
558 return gtk_window_activate_key (win, ev);