plugin selector from doug; lots and lots of fixes from karsten
[ardour.git] / gtk2_ardour / utils.cc
1 /*
2     Copyright (C) 2003 Paul Davis 
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20
21 #include <cstdlib>
22 #include <cctype>
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>
29
30 #include <gtkmm2ext/utils.h>
31
32 #include "ardour_ui.h"
33 #include "utils.h"
34 #include "i18n.h"
35 #include "rgb_macros.h"
36
37 using namespace std;
38 using namespace Gtk;
39 using namespace sigc;
40
41 string
42 short_version (string orig, string::size_type target_length)
43 {
44         /* this tries to create a recognizable abbreviation
45            of "orig" by removing characters until we meet
46            a certain target length.
47
48            note that we deliberately leave digits in the result
49            without modification.
50         */
51
52
53         string::size_type pos;
54
55         /* remove white-space and punctuation, starting at end */
56
57         while (orig.length() > target_length) {
58                 if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
59                         break;
60                 }
61                 orig.replace (pos, 1, "");
62         }
63
64         /* remove lower-case vowels, starting at end */
65
66         while (orig.length() > target_length) {
67                 if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
68                         break;
69                 }
70                 orig.replace (pos, 1, "");
71         }
72
73         /* remove upper-case vowels, starting at end */
74
75         while (orig.length() > target_length) {
76                 if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
77                         break;
78                 }
79                 orig.replace (pos, 1, "");
80         }
81
82         /* remove lower-case consonants, starting at end */
83
84         while (orig.length() > target_length) {
85                 if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
86                         break;
87                 }
88                 orig.replace (pos, 1, "");
89         }
90
91         /* remove upper-case consonants, starting at end */
92
93         while (orig.length() > target_length) {
94                 if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
95                         break;
96                 }
97                 orig.replace (pos, 1, "");
98         }
99
100         /* whatever the length is now, use it */
101         
102         return orig;
103 }
104
105 string
106 fit_to_pixels (string str, int pixel_width, string font)
107 {
108         Label foo;
109         int width;
110         int height;
111         Pango::FontDescription fontdesc (font);
112         
113         int namelen = str.length();
114         char cstr[namelen+1];
115         strcpy (cstr, str.c_str());
116         
117         while (namelen) {
118                 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (cstr);
119
120                 layout->set_font_description (fontdesc);
121                 layout->get_pixel_size (width, height);
122
123                 if (width < (pixel_width)) {
124                         break;
125                 }
126
127                 --namelen;
128                 cstr[namelen] = '\0';
129
130         }
131
132         return cstr;
133 }
134
135 int
136 atoi (const string& s)
137 {
138         return atoi (s.c_str());
139 }
140
141 double
142 atof (const string& s)
143 {
144         return atof (s.c_str());
145 }
146
147 void
148 strip_whitespace_edges (string& str)
149 {
150         string::size_type i;
151         string::size_type len;
152         string::size_type s;
153
154         len = str.length();
155
156         for (i = 0; i < len; ++i) {
157                 if (isgraph (str[i])) {
158                         break;
159                 }
160         }
161
162         s = i;
163
164         for (i = len - 1; i >= 0; --i) {
165                 if (isgraph (str[i])) {
166                         break;
167                 }
168         }
169
170         str = str.substr (s, (i - s) + 1);
171 }
172
173 vector<string>
174 internationalize (const char **array)
175 {
176         vector<string> v;
177
178         for (uint32_t i = 0; array[i]; ++i) {
179                 v.push_back (_(array[i]));
180         }
181
182         return v;
183 }
184
185 gint
186 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
187 {
188         ARDOUR_UI::instance()->allow_focus (false);
189         win->hide_all ();
190         return TRUE;
191 }
192
193 void
194 allow_keyboard_focus (bool yn)
195 {
196         ARDOUR_UI::instance()->allow_focus (yn);
197 }
198
199 /* xpm2rgb copied from nixieclock, which bore the legend:
200
201     nixieclock - a nixie desktop timepiece
202     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
203
204     and was released under the GPL.
205 */
206
207 unsigned char*
208 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
209 {
210         static long vals[256], val;
211         uint32_t t, x, y, colors, cpp;
212         unsigned char c;
213         unsigned char *savergb, *rgb;
214         
215         // PARSE HEADER
216         
217         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
218                 error << string_compose (_("bad XPM header %1"), xpm[0])
219                       << endmsg;
220                 return 0;
221         }
222
223         savergb = rgb = (unsigned char*)art_alloc (h * w * 3);
224         
225         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
226         for (t = 0; t < colors; ++t) {
227                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
228                 vals[c] = val;
229         }
230         
231         // COLORMAP -> RGB CONVERSION
232         //    Get low 3 bytes from vals[]
233         //
234
235         const char *p;
236         for (y = h-1; y > 0; --y) {
237
238                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
239                         val = vals[(int)*p++];
240                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
241                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
242                         *(rgb+0) = val & 0xff;             // 0:R
243                 }
244         }
245
246         return (savergb);
247 }
248
249 unsigned char*
250 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
251 {
252         static long vals[256], val;
253         uint32_t t, x, y, colors, cpp;
254         unsigned char c;
255         unsigned char *savergb, *rgb;
256         char transparent;
257
258         // PARSE HEADER
259
260         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
261                 error << string_compose (_("bad XPM header %1"), xpm[0])
262                       << endmsg;
263                 return 0;
264         }
265
266         savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
267         
268         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
269
270         if (strstr (xpm[1], "None")) {
271                 sscanf (xpm[1], "%c", &transparent);
272                 t = 1;
273         } else {
274                 transparent = 0;
275                 t = 0;
276         }
277
278         for (; t < colors; ++t) {
279                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
280                 vals[c] = val;
281         }
282         
283         // COLORMAP -> RGB CONVERSION
284         //    Get low 3 bytes from vals[]
285         //
286
287         const char *p;
288         for (y = h-1; y > 0; --y) {
289
290                 char alpha;
291
292                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
293
294                         if (transparent && (*p++ == transparent)) {
295                                 alpha = 0;
296                                 val = 0;
297                         } else {
298                                 alpha = 255;
299                                 val = vals[(int)*p];
300                         }
301
302                         *(rgb+3) = alpha;                  // 3: alpha
303                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
304                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
305                         *(rgb+0) = val & 0xff;             // 0:R
306                 }
307         }
308
309         return (savergb);
310 }
311
312 Gnome::Canvas::Points*
313 get_canvas_points (string who, uint32_t npoints)
314 {
315         // cerr << who << ": wants " << npoints << " canvas points" << endl;
316 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
317         if (npoints > (uint32_t) gdk_screen_width() + 4) {
318                 abort ();
319         }
320 #endif
321         return new Gnome::Canvas::Points (npoints);
322 }
323
324 int
325 channel_combo_get_channel_count (Gtk::Combo& combo)
326 {
327         string str = combo.get_entry()->get_text();
328         int chns;
329
330         if (str == _("mono")) {
331                 return 1;
332         } else if (str == _("stereo")) {
333                 return 2;
334         } else if ((chns = atoi (str)) != 0) {
335                 return chns;
336         } else {
337                 return 0;
338         }
339 }
340
341 static int32_t 
342 int_from_hex (char hic, char loc) 
343 {
344         int hi;         /* hi byte */
345         int lo;         /* low byte */
346
347         hi = (int) hic;
348
349         if( ('0'<=hi) && (hi<='9') ) {
350                 hi -= '0';
351         } else if( ('a'<= hi) && (hi<= 'f') ) {
352                 hi -= ('a'-10);
353         } else if( ('A'<=hi) && (hi<='F') ) {
354                 hi -= ('A'-10);
355         }
356         
357         lo = (int) loc;
358         
359         if( ('0'<=lo) && (lo<='9') ) {
360                 lo -= '0';
361         } else if( ('a'<=lo) && (lo<='f') ) {
362                 lo -= ('a'-10);
363         } else if( ('A'<=lo) && (lo<='F') ) {
364                 lo -= ('A'-10);
365         }
366
367         return lo + (16 * hi);
368 }
369
370 void
371 url_decode (string& url)
372 {
373         string::iterator last;
374         string::iterator next;
375
376         for (string::iterator i = url.begin(); i != url.end(); ++i) {
377                 if ((*i) == '+') {
378                         *i = ' ';
379                 }
380         }
381
382         if (url.length() <= 3) {
383                 return;
384         }
385
386         last = url.end();
387
388         --last; /* points at last char */
389         --last; /* points at last char - 1 */
390
391         for (string::iterator i = url.begin(); i != last; ) {
392
393                 if (*i == '%') {
394
395                         next = i;
396
397                         url.erase (i);
398                         
399                         i = next;
400                         ++next;
401                         
402                         if (isxdigit (*i) && isxdigit (*next)) {
403                                 /* replace first digit with char */
404                                 *i = int_from_hex (*i,*next);
405                                 ++i; /* points at 2nd of 2 digits */
406                                 url.erase (i);
407                         }
408                 } else {
409                         ++i;
410                 }
411         }
412 }
413
414 Pango::FontDescription
415 get_font_for_style (string widgetname)
416 {
417         Gtk::Label foobar;
418         Glib::RefPtr<Style> style;
419
420         foobar.set_name (widgetname);
421         foobar.ensure_style();
422
423         style = foobar.get_style ();
424         return style->get_font();
425 }
426
427 gint
428 pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
429 {
430         if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
431                 return FALSE;
432         }
433
434         if (Keyboard::is_delete_event (ev)) {
435
436                 gint pos;
437                 gint cmp;
438                 
439                 pos = pane->get_position ();
440
441                 if (dynamic_cast<VPaned*>(pane)) {
442                         cmp = pane->get_height();
443                 } else {
444                         cmp = pane->get_width();
445                 }
446
447                 /* we have to use approximations here because we can't predict the
448                    exact position or sizes of the pane (themes, etc)
449                 */
450
451                 if (pos < 10 || abs (pos - cmp) < 10) {
452
453                         /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
454                         
455                         pane->set_position ((gint64) pane->get_data ("rpos"));
456
457                 } else {        
458
459                         int collapse_direction;
460
461                         /* store the current position */
462
463                         pane->set_data ("rpos", (gpointer) pos);
464
465                         /* collapse to show the relevant child in full */
466                         
467                         collapse_direction = (gint64) pane->get_data ("collapse-direction");
468
469                         if (collapse_direction) {
470                                 pane->set_position (1);
471                         } else {
472                                 if (dynamic_cast<VPaned*>(pane)) {
473                                         pane->set_position (pane->get_height());
474                                 } else {
475                                         pane->set_position (pane->get_width());
476                                 }
477                         }
478                 }
479
480                 return TRUE;
481         } 
482
483         return FALSE;
484 }
485 uint32_t
486 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a)
487 {
488         Gtk::Label foo;
489         
490         foo.set_name (style);
491         foo.ensure_style ();
492         
493         GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
494
495         if (waverc) {
496                 r = waverc->fg[Gtk::STATE_NORMAL].red / 257;
497                 g = waverc->fg[Gtk::STATE_NORMAL].green / 257;
498                 b = waverc->fg[Gtk::STATE_NORMAL].blue / 257;
499
500                 /* what a hack ... "a" is for "active" */
501
502                 a = waverc->fg[GTK_STATE_ACTIVE].red / 257; 
503
504         } else {
505                 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
506         }
507         
508         return (uint32_t) RGBA_TO_UINT(r,g,b,a);
509 }
510
511 void
512 decorate (Gtk::Window& w, Gdk::WMDecoration d)
513 {
514         w.get_window()->set_decorations (d);
515 }