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 channel_combo_get_channel_count (Gtk::ComboBoxText& combo)
297 string str = combo.get_active_text();
300 if (str == _("mono")) {
302 } else if (str == _("stereo")) {
304 } else if ((chns = atoi (str)) != 0) {
312 int_from_hex (char hic, char loc)
314 int hi; /* hi byte */
315 int lo; /* low byte */
319 if( ('0'<=hi) && (hi<='9') ) {
321 } else if( ('a'<= hi) && (hi<= 'f') ) {
323 } else if( ('A'<=hi) && (hi<='F') ) {
329 if( ('0'<=lo) && (lo<='9') ) {
331 } else if( ('a'<=lo) && (lo<='f') ) {
333 } else if( ('A'<=lo) && (lo<='F') ) {
337 return lo + (16 * hi);
341 url_decode (string& url)
343 string::iterator last;
344 string::iterator next;
346 for (string::iterator i = url.begin(); i != url.end(); ++i) {
352 if (url.length() <= 3) {
358 --last; /* points at last char */
359 --last; /* points at last char - 1 */
361 for (string::iterator i = url.begin(); i != last; ) {
372 if (isxdigit (*i) && isxdigit (*next)) {
373 /* replace first digit with char */
374 *i = int_from_hex (*i,*next);
375 ++i; /* points at 2nd of 2 digits */
384 Pango::FontDescription
385 get_font_for_style (string widgetname)
387 Gtk::Window window (WINDOW_TOPLEVEL);
389 Glib::RefPtr<Style> style;
392 foobar.set_name (widgetname);
393 foobar.ensure_style();
395 style = foobar.get_style ();
396 return style->get_font();
400 pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
402 if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
406 if (Keyboard::is_delete_event (ev)) {
411 pos = pane->get_position ();
413 if (dynamic_cast<VPaned*>(pane)) {
414 cmp = pane->get_height();
416 cmp = pane->get_width();
419 /* we have to use approximations here because we can't predict the
420 exact position or sizes of the pane (themes, etc)
423 if (pos < 10 || abs (pos - cmp) < 10) {
425 /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
427 pane->set_position ((gint64) pane->get_data ("rpos"));
431 int collapse_direction;
433 /* store the current position */
435 pane->set_data ("rpos", (gpointer) pos);
437 /* collapse to show the relevant child in full */
439 collapse_direction = (gint64) pane->get_data ("collapse-direction");
441 if (collapse_direction) {
442 pane->set_position (1);
444 if (dynamic_cast<VPaned*>(pane)) {
445 pane->set_position (pane->get_height());
447 pane->set_position (pane->get_width());
458 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
460 /* In GTK+2, styles aren't set up correctly if the widget is not
461 attached to a toplevel window that has a screen pointer.
464 static Gtk::Window* window = 0;
467 window = new Window (WINDOW_TOPLEVEL);
474 foo.set_name (style);
477 GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
481 r = waverc->fg[state].red / 257;
482 g = waverc->fg[state].green / 257;
483 b = waverc->fg[state].blue / 257;
484 /* what a hack ... "a" is for "active" */
485 if (state == Gtk::STATE_NORMAL && rgba) {
486 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
488 } else if (attr == "bg") {
490 r = waverc->bg[state].red / 257;
491 g = waverc->bg[state].green / 257;
492 b = waverc->bg[state].blue / 257;
493 } else if (attr == "base") {
494 r = waverc->base[state].red / 257;
495 g = waverc->base[state].green / 257;
496 b = waverc->base[state].blue / 257;
497 } else if (attr == "text") {
498 r = waverc->text[state].red / 257;
499 g = waverc->text[state].green / 257;
500 b = waverc->text[state].blue / 257;
503 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
508 if (state == Gtk::STATE_NORMAL && rgba) {
509 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
511 return (uint32_t) RGB_TO_UINT(r,g,b);
516 canvas_item_visible (ArdourCanvas::Item* item)
518 return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
522 set_color (Gdk::Color& c, int rgb)
524 c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);