semi-fix xfade display, and reduce number of calls to Curve::get_vector() by a factor...
[ardour.git] / gtk2_ardour / utils.cc
1 /*
2     Copyright (C) 2003 Paul Davis
3
4     This program is free software; you an 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 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
25 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
26
27 #include <cstdlib>
28 #include <clocale>
29 #include <cstring>
30 #include <cctype>
31 #include <fstream>
32 #include <list>
33 #include <sys/stat.h>
34 #include <gtkmm/rc.h>
35 #include <gtkmm/window.h>
36 #include <gtkmm/combo.h>
37 #include <gtkmm/label.h>
38 #include <gtkmm/paned.h>
39 #include <gtk/gtkpaned.h>
40 #include <boost/algorithm/string.hpp>
41
42 #include "pbd/file_utils.h"
43
44 #include <gtkmm2ext/utils.h>
45 #include "ardour/rc_configuration.h"
46 #include "ardour/filesystem_paths.h"
47 #include "canvas/item.h"
48
49 #include "ardour_ui.h"
50 #include "debug.h"
51 #include "public_editor.h"
52 #include "keyboard.h"
53 #include "utils.h"
54 #include "i18n.h"
55 #include "rgb_macros.h"
56 #include "gui_thread.h"
57
58 using namespace std;
59 using namespace Gtk;
60 using namespace Glib;
61 using namespace PBD;
62 using Gtkmm2ext::Keyboard;
63
64 sigc::signal<void>  DPIReset;
65
66
67 /** Add an element to a menu, settings its sensitivity.
68  * @param m Menu to add to.
69  * @param e Element to add.
70  * @param s true to make sensitive, false to make insensitive
71  */
72 void
73 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
74 {
75         m.push_back (e);
76         if (!s) {
77                 m.back().set_sensitive (false);
78         }
79 }
80
81
82 gint
83 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
84 {
85         win->hide ();
86         return 0;
87 }
88
89 /* xpm2rgb copied from nixieclock, which bore the legend:
90
91     nixieclock - a nixie desktop timepiece
92     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
93
94     and was released under the GPL.
95 */
96
97 unsigned char*
98 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
99 {
100         static long vals[256], val;
101         uint32_t t, x, y, colors, cpp;
102         unsigned char c;
103         unsigned char *savergb, *rgb;
104
105         // PARSE HEADER
106
107         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
108                 error << string_compose (_("bad XPM header %1"), xpm[0])
109                       << endmsg;
110                 return 0;
111         }
112
113         savergb = rgb = (unsigned char*) malloc (h * w * 3);
114
115         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
116         for (t = 0; t < colors; ++t) {
117                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
118                 vals[c] = val;
119         }
120
121         // COLORMAP -> RGB CONVERSION
122         //    Get low 3 bytes from vals[]
123         //
124
125         const char *p;
126         for (y = h-1; y > 0; --y) {
127
128                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
129                         val = vals[(int)*p++];
130                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
131                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
132                         *(rgb+0) = val & 0xff;             // 0:R
133                 }
134         }
135
136         return (savergb);
137 }
138
139 unsigned char*
140 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
141 {
142         static long vals[256], val;
143         uint32_t t, x, y, colors, cpp;
144         unsigned char c;
145         unsigned char *savergb, *rgb;
146         char transparent;
147
148         // PARSE HEADER
149
150         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
151                 error << string_compose (_("bad XPM header %1"), xpm[0])
152                       << endmsg;
153                 return 0;
154         }
155
156         savergb = rgb = (unsigned char*) malloc (h * w * 4);
157
158         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
159
160         if (strstr (xpm[1], "None")) {
161                 sscanf (xpm[1], "%c", &transparent);
162                 t = 1;
163         } else {
164                 transparent = 0;
165                 t = 0;
166         }
167
168         for (; t < colors; ++t) {
169                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
170                 vals[c] = val;
171         }
172
173         // COLORMAP -> RGB CONVERSION
174         //    Get low 3 bytes from vals[]
175         //
176
177         const char *p;
178         for (y = h-1; y > 0; --y) {
179
180                 char alpha;
181
182                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
183
184                         if (transparent && (*p++ == transparent)) {
185                                 alpha = 0;
186                                 val = 0;
187                         } else {
188                                 alpha = 255;
189                                 val = vals[(int)*p];
190                         }
191
192                         *(rgb+3) = alpha;                  // 3: alpha
193                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
194                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
195                         *(rgb+0) = val & 0xff;             // 0:R
196                 }
197         }
198
199         return (savergb);
200 }
201
202 Pango::FontDescription
203 get_font_for_style (string widgetname)
204 {
205         Gtk::Window window (WINDOW_TOPLEVEL);
206         Gtk::Label foobar;
207         Glib::RefPtr<Gtk::Style> style;
208
209         window.add (foobar);
210         foobar.set_name (widgetname);
211         foobar.ensure_style();
212
213         style = foobar.get_style ();
214
215         Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
216
217         PangoFontDescription *pfd = const_cast<PangoFontDescription *> (pango_layout_get_font_description(const_cast<PangoLayout *>(layout->gobj())));
218
219         if (!pfd) {
220
221                 /* layout inherited its font description from a PangoContext */
222
223                 PangoContext* ctxt = (PangoContext*) pango_layout_get_context (const_cast<PangoLayout*>(layout->gobj()));
224                 pfd =  pango_context_get_font_description (ctxt);
225                 return Pango::FontDescription (pfd); /* make a copy */
226         }
227
228         return Pango::FontDescription (pfd); /* make a copy */
229 }
230
231 uint32_t
232 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
233 {
234         /* In GTK+2, styles aren't set up correctly if the widget is not
235            attached to a toplevel window that has a screen pointer.
236         */
237
238         static Gtk::Window* window = 0;
239
240         if (window == 0) {
241                 window = new Window (WINDOW_TOPLEVEL);
242         }
243
244         Gtk::Label foo;
245
246         window->add (foo);
247
248         foo.set_name (style);
249         foo.ensure_style ();
250
251         GtkRcStyle* rc = foo.get_style()->gobj()->rc_style;
252
253         if (rc) {
254                 if (attr == "fg") {
255                         r = rc->fg[state].red / 257;
256                         g = rc->fg[state].green / 257;
257                         b = rc->fg[state].blue / 257;
258
259                         /* what a hack ... "a" is for "active" */
260                         if (state == Gtk::STATE_NORMAL && rgba) {
261                                 a = rc->fg[GTK_STATE_ACTIVE].red / 257;
262                         }
263                 } else if (attr == "bg") {
264                         r = g = b = 0;
265                         r = rc->bg[state].red / 257;
266                         g = rc->bg[state].green / 257;
267                         b = rc->bg[state].blue / 257;
268                 } else if (attr == "base") {
269                         r = rc->base[state].red / 257;
270                         g = rc->base[state].green / 257;
271                         b = rc->base[state].blue / 257;
272                 } else if (attr == "text") {
273                         r = rc->text[state].red / 257;
274                         g = rc->text[state].green / 257;
275                         b = rc->text[state].blue / 257;
276                 }
277         } else {
278                 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
279         }
280
281         window->remove ();
282
283         if (state == Gtk::STATE_NORMAL && rgba) {
284                 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
285         } else {
286                 return (uint32_t) RGB_TO_UINT(r,g,b);
287         }
288 }
289
290 void
291 set_color (Gdk::Color& c, int rgb)
292 {
293         c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
294 }
295
296 bool
297 relay_key_press (GdkEventKey* ev, Gtk::Window* win)
298 {
299         if (!key_press_focus_accelerator_handler (*win, ev)) {
300                 return PublicEditor::instance().on_key_press_event(ev);
301         } else {
302                 return true;
303         }
304 }
305
306 bool
307 forward_key_press (GdkEventKey* ev)
308 {
309         return PublicEditor::instance().on_key_press_event(ev);
310 }
311
312 bool
313 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
314 {
315         GtkWindow* win = window.gobj();
316         GtkWidget* focus = gtk_window_get_focus (win);
317         bool special_handling_of_unmodified_accelerators = false;
318         bool allow_activating = true;
319         /* consider all relevant modifiers but not LOCK or SHIFT */
320         const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
321
322         if (focus) {
323                 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
324                         special_handling_of_unmodified_accelerators = true;
325                 }
326         }
327
328 #ifdef GTKOSX
329         /* at one time this appeared to be necessary. As of July 2012, it does not
330            appear to be. if it ever is necessar, figure out if it should apply
331            to all platforms.
332         */
333 #if 0 
334         if (Keyboard::some_magic_widget_has_focus ()) {
335                 allow_activating = false;
336         }
337 #endif
338 #endif
339
340
341         DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 focus = %7 Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 allow_activation ? %6\n",
342                                                           win,
343                                                           ev->keyval,
344                                                           ev->state,
345                                                           special_handling_of_unmodified_accelerators,
346                                                           Keyboard::some_magic_widget_has_focus(),
347                                                           allow_activating,
348                                                           focus));
349
350         /* This exists to allow us to override the way GTK handles
351            key events. The normal sequence is:
352
353            a) event is delivered to a GtkWindow
354            b) accelerators/mnemonics are activated
355            c) if (b) didn't handle the event, propagate to
356                the focus widget and/or focus chain
357
358            The problem with this is that if the accelerators include
359            keys without modifiers, such as the space bar or the
360            letter "e", then pressing the key while typing into
361            a text entry widget results in the accelerator being
362            activated, instead of the desired letter appearing
363            in the text entry.
364
365            There is no good way of fixing this, but this
366            represents a compromise. The idea is that
367            key events involving modifiers (not Shift)
368            get routed into the activation pathway first, then
369            get propagated to the focus widget if necessary.
370
371            If the key event doesn't involve modifiers,
372            we deliver to the focus widget first, thus allowing
373            it to get "normal text" without interference
374            from acceleration.
375
376            Of course, this can also be problematic: if there
377            is a widget with focus, then it will swallow
378            all "normal text" accelerators.
379         */
380
381         if (!special_handling_of_unmodified_accelerators) {
382
383                 /* XXX note that for a brief moment, the conditional above
384                  * included "|| (ev->state & mask)" so as to enforce the
385                  * implication of special_handling_of_UNMODIFIED_accelerators.
386                  * however, this forces any key that GTK doesn't allow and that
387                  * we have an alternative (see next comment) for to be
388                  * automatically sent through the accel groups activation
389                  * pathway, which prevents individual widgets & canvas items
390                  * from ever seeing it if is used by a key binding.
391                  * 
392                  * specifically, this hid Ctrl-down-arrow from MIDI region
393                  * views because it is also bound to an action.
394                  *
395                  * until we have a robust, clean binding system, this
396                  * quirk will have to remain in place.
397                  */
398
399                 /* pretend that certain key events that GTK does not allow
400                    to be used as accelerators are actually something that
401                    it does allow. but only where there are no modifiers.
402                 */
403
404                 uint32_t fakekey = ev->keyval;
405
406                 if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
407                         DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tactivate (was %1 now %2) without special hanlding of unmodified accels\n",
408                                                                           ev->keyval, fakekey));
409
410                         GdkModifierType mod = GdkModifierType (ev->state);
411
412                         mod = GdkModifierType (mod & gtk_accelerator_get_default_mod_mask());
413 #ifdef GTKOSX
414                         /* GTK on OS X is currently (February 2012) setting both
415                            the Meta and Mod2 bits in the event modifier state if 
416                            the Command key is down.
417
418                            gtk_accel_groups_activate() does not invoke any of the logic
419                            that gtk_window_activate_key() will that sorts out that stupid
420                            state of affairs, and as a result it fails to find a match
421                            for the key event and the current set of accelerators.
422
423                            to fix this, if the meta bit is set, remove the mod2 bit
424                            from the modifier. this assumes that our bindings use Primary
425                            which will have set the meta bit in the accelerator entry.
426                         */
427                         if (mod & GDK_META_MASK) {
428                                 mod = GdkModifierType (mod & ~GDK_MOD2_MASK);
429                         }
430 #endif
431
432                         if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, mod)) {
433                                 DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n");
434                                 return true;
435                         }
436                 }
437         }
438
439         if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
440
441                 /* no special handling or there are modifiers in effect: accelerate first */
442
443                 DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
444
445                 if (allow_activating) {
446                         DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
447                         if (gtk_window_activate_key (win, ev)) {
448                                 DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
449                                 return true;
450                         }
451                 } else {
452                         DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
453                 }
454
455                 DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
456
457                 return gtk_window_propagate_key_event (win, ev);
458         }
459
460         /* no modifiers, propagate first */
461
462         DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
463
464         if (!gtk_window_propagate_key_event (win, ev)) {
465                 DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
466                 if (allow_activating) {
467                         return gtk_window_activate_key (win, ev);
468                 } else {
469                         DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
470                 }
471
472         } else {
473                 DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
474                 return true;
475         }
476
477         DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
478         return true;
479 }
480
481 Glib::RefPtr<Gdk::Pixbuf>
482 get_xpm (std::string name)
483 {
484         if (!xpm_map[name]) {
485
486                 SearchPath spath(ARDOUR::ardour_data_search_path());
487
488                 spath.add_subdirectory_to_paths("pixmaps");
489
490                 std::string data_file_path;
491
492                 if(!find_file_in_search_path (spath, name, data_file_path)) {
493                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
494                 }
495
496                 try {
497                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path);
498                 } catch(const Glib::Error& e)   {
499                         warning << "Caught Glib::Error: " << e.what() << endmsg;
500                 }
501         }
502
503         return xpm_map[name];
504 }
505
506 std::string
507 get_icon_path (const char* cname)
508 {
509         string name = cname;
510         name += X_(".png");
511
512         SearchPath spath(ARDOUR::ardour_data_search_path());
513
514         spath.add_subdirectory_to_paths("icons");
515
516         std::string data_file_path;
517
518         if (!find_file_in_search_path (spath, name, data_file_path)) {
519                 fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
520         }
521
522         return data_file_path;
523 }
524
525 Glib::RefPtr<Gdk::Pixbuf>
526 get_icon (const char* cname)
527 {
528         Glib::RefPtr<Gdk::Pixbuf> img;
529         try {
530                 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
531         } catch (const Gdk::PixbufError &e) {
532                 cerr << "Caught PixbufError: " << e.what() << endl;
533         } catch (...) {
534                 error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg;
535         }
536
537         return img;
538 }
539
540 string
541 longest (vector<string>& strings)
542 {
543         if (strings.empty()) {
544                 return string ("");
545         }
546
547         vector<string>::iterator longest = strings.begin();
548         string::size_type longest_length = (*longest).length();
549
550         vector<string>::iterator i = longest;
551         ++i;
552
553         while (i != strings.end()) {
554
555                 string::size_type len = (*i).length();
556
557                 if (len > longest_length) {
558                         longest = i;
559                         longest_length = len;
560                 }
561
562                 ++i;
563         }
564
565         return *longest;
566 }
567
568 bool
569 key_is_legal_for_numeric_entry (guint keyval)
570 {
571         /* we assume that this does not change over the life of the process 
572          */
573
574         static int comma_decimal = -1;
575
576         switch (keyval) {
577         case GDK_period:
578         case GDK_comma:
579                 if (comma_decimal < 0) {
580                         std::lconv* lc = std::localeconv();
581                         if (strchr (lc->decimal_point, ',') != 0) {
582                                 comma_decimal = 1;
583                         } else {
584                                 comma_decimal = 0;
585                         }
586                 }
587                 break;
588         default:
589                 break;
590         }
591
592         switch (keyval) {
593         case GDK_decimalpoint:
594         case GDK_KP_Separator:
595                 return true;
596
597         case GDK_period:
598                 if (comma_decimal) {
599                         return false;
600                 } else {
601                         return true;
602                 }
603                 break;
604         case GDK_comma:
605                 if (comma_decimal) {
606                         return true;
607                 } else {
608                         return false;
609                 }
610                 break;
611         case GDK_minus:
612         case GDK_plus:
613         case GDK_0:
614         case GDK_1:
615         case GDK_2:
616         case GDK_3:
617         case GDK_4:
618         case GDK_5:
619         case GDK_6:
620         case GDK_7:
621         case GDK_8:
622         case GDK_9:
623         case GDK_KP_Add:
624         case GDK_KP_Subtract:
625         case GDK_KP_Decimal:
626         case GDK_KP_0:
627         case GDK_KP_1:
628         case GDK_KP_2:
629         case GDK_KP_3:
630         case GDK_KP_4:
631         case GDK_KP_5:
632         case GDK_KP_6:
633         case GDK_KP_7:
634         case GDK_KP_8:
635         case GDK_KP_9:
636         case GDK_Return:
637         case GDK_BackSpace:
638         case GDK_Delete:
639         case GDK_KP_Enter:
640         case GDK_Home:
641         case GDK_End:
642         case GDK_Left:
643         case GDK_Right:
644                 return true;
645
646         default:
647                 break;
648         }
649
650         return false;
651 }
652 void
653 set_pango_fontsize ()
654 {
655         long val = ARDOUR::Config->get_font_scale();
656
657         /* FT2 rendering - used by GnomeCanvas, sigh */
658
659         pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_new(), val/1024, val/1024);
660
661         /* Cairo rendering, in case there is any */
662
663         pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
664 }
665
666 void
667 reset_dpi ()
668 {
669         long val = ARDOUR::Config->get_font_scale();
670         set_pango_fontsize ();
671         /* Xft rendering */
672
673         gtk_settings_set_long_property (gtk_settings_get_default(),
674                                         "gtk-xft-dpi", val, "ardour");
675         DPIReset();//Emit Signal
676 }
677
678 void
679 resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
680 {
681         Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
682         Gdk::Rectangle monitor_rect;
683         screen->get_monitor_geometry (0, monitor_rect);
684
685         int const w = std::min (int (monitor_rect.get_width() * 0.8), max_width);
686         int const h = std::min (int (monitor_rect.get_height() * 0.8), max_height);
687
688         window->resize (w, h);
689 }
690
691
692 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
693 string
694 escape_underscores (string const & s)
695 {
696         string o;
697         string::size_type const N = s.length ();
698
699         for (string::size_type i = 0; i < N; ++i) {
700                 if (s[i] == '_') {
701                         o += "__";
702                 } else {
703                         o += s[i];
704                 }
705         }
706
707         return o;
708 }
709
710 /** Replace < and > with &lt; and &gt; respectively to make < > display correctly in markup strings */
711 string
712 escape_angled_brackets (string const & s)
713 {
714         string o = s;
715         boost::replace_all (o, "<", "&lt;");
716         boost::replace_all (o, ">", "&gt;");
717         return o;
718 }
719
720 Gdk::Color
721 unique_random_color (list<Gdk::Color>& used_colors)
722 {
723         Gdk::Color newcolor;
724
725         while (1) {
726
727                 /* avoid neon/glowing tones by limiting them to the
728                    "inner section" (paler) of a color wheel/circle.
729                 */
730
731                 const int32_t max_saturation = 48000; // 65535 would open up the whole color wheel
732
733                 newcolor.set_red (random() % max_saturation);
734                 newcolor.set_blue (random() % max_saturation);
735                 newcolor.set_green (random() % max_saturation);
736
737                 if (used_colors.size() == 0) {
738                         used_colors.push_back (newcolor);
739                         return newcolor;
740                 }
741
742                 for (list<Gdk::Color>::iterator i = used_colors.begin(); i != used_colors.end(); ++i) {
743                   Gdk::Color c = *i;
744                         float rdelta, bdelta, gdelta;
745
746                         rdelta = newcolor.get_red() - c.get_red();
747                         bdelta = newcolor.get_blue() - c.get_blue();
748                         gdelta = newcolor.get_green() - c.get_green();
749
750                         if (sqrt (rdelta*rdelta + bdelta*bdelta + gdelta*gdelta) > 25.0) {
751                                 used_colors.push_back (newcolor);
752                                 return newcolor;
753                         }
754                 }
755
756                 /* XXX need throttle here to make sure we don't spin for ever */
757         }
758 }