enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[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 <cstdlib>
25 #include <clocale>
26 #include <cstring>
27 #include <cctype>
28 #include <cmath>
29 #include <list>
30 #include <sys/stat.h>
31 #include <gtkmm/rc.h>
32 #include <gtkmm/window.h>
33 #include <gtkmm/combo.h>
34 #include <gtkmm/label.h>
35 #include <gtkmm/paned.h>
36 #include <gtk/gtkpaned.h>
37 #include <boost/algorithm/string.hpp>
38
39 #include "pbd/basename.h"
40 #include "pbd/file_utils.h"
41 #include "pbd/stacktrace.h"
42
43 #include <gtkmm2ext/utils.h>
44
45 #include "ardour/filesystem_paths.h"
46 #include "ardour/search_paths.h"
47
48 #include "canvas/item.h"
49 #include "canvas/utils.h"
50
51 #include "debug.h"
52 #include "public_editor.h"
53 #include "keyboard.h"
54 #include "utils.h"
55 #include "pbd/i18n.h"
56 #include "rgb_macros.h"
57 #include "gui_thread.h"
58 #include "ui_config.h"
59 #include "ardour_dialog.h"
60 #include "ardour_ui.h"
61
62 using namespace std;
63 using namespace Gtk;
64 using namespace Glib;
65 using namespace PBD;
66 using Gtkmm2ext::Keyboard;
67
68 namespace ARDOUR_UI_UTILS {
69         sigc::signal<void>  DPIReset;
70 }
71
72 #ifdef PLATFORM_WINDOWS
73 #define random() rand()
74 #endif
75
76
77 /** Add an element to a menu, settings its sensitivity.
78  * @param m Menu to add to.
79  * @param e Element to add.
80  * @param s true to make sensitive, false to make insensitive
81  */
82 void
83 ARDOUR_UI_UTILS::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
84 {
85         m.push_back (e);
86         if (!s) {
87                 m.back().set_sensitive (false);
88         }
89 }
90
91
92 gint
93 ARDOUR_UI_UTILS::just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
94 {
95         win->hide ();
96         return 0;
97 }
98
99 /* xpm2rgb copied from nixieclock, which bore the legend:
100
101     nixieclock - a nixie desktop timepiece
102     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
103
104     and was released under the GPL.
105 */
106
107 unsigned char*
108 ARDOUR_UI_UTILS::xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
109 {
110         static long vals[256], val;
111         uint32_t t, x, y, colors, cpp;
112         unsigned char c;
113         unsigned char *savergb, *rgb;
114
115         // PARSE HEADER
116
117         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
118                 error << string_compose (_("bad XPM header %1"), xpm[0])
119                       << endmsg;
120                 return 0;
121         }
122
123         savergb = rgb = (unsigned char*) malloc (h * w * 3);
124
125         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
126         for (t = 0; t < colors; ++t) {
127                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
128                 vals[c] = val;
129         }
130
131         // COLORMAP -> RGB CONVERSION
132         //    Get low 3 bytes from vals[]
133         //
134
135         const char *p;
136         for (y = h-1; y > 0; --y) {
137
138                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
139                         val = vals[(int)*p++];
140                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
141                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
142                         *(rgb+0) = val & 0xff;             // 0:R
143                 }
144         }
145
146         return (savergb);
147 }
148
149 unsigned char*
150 ARDOUR_UI_UTILS::xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
151 {
152         static long vals[256], val;
153         uint32_t t, x, y, colors, cpp;
154         unsigned char c;
155         unsigned char *savergb, *rgb;
156         char transparent;
157
158         // PARSE HEADER
159
160         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
161                 error << string_compose (_("bad XPM header %1"), xpm[0])
162                       << endmsg;
163                 return 0;
164         }
165
166         savergb = rgb = (unsigned char*) malloc (h * w * 4);
167
168         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
169
170         if (strstr (xpm[1], "None")) {
171                 sscanf (xpm[1], "%c", &transparent);
172                 t = 1;
173         } else {
174                 transparent = 0;
175                 t = 0;
176         }
177
178         for (; t < colors; ++t) {
179                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
180                 vals[c] = val;
181         }
182
183         // COLORMAP -> RGB CONVERSION
184         //    Get low 3 bytes from vals[]
185         //
186
187         const char *p;
188         for (y = h-1; y > 0; --y) {
189
190                 char alpha;
191
192                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
193
194                         if (transparent && (*p++ == transparent)) {
195                                 alpha = 0;
196                                 val = 0;
197                         } else {
198                                 alpha = 255;
199                                 val = vals[(int)*p];
200                         }
201
202                         *(rgb+3) = alpha;                  // 3: alpha
203                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
204                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
205                         *(rgb+0) = val & 0xff;             // 0:R
206                 }
207         }
208
209         return (savergb);
210 }
211
212 /** Returns a Pango::FontDescription given a string describing the font.
213  *
214  * If the returned FontDescription does not specify a family, then
215  * the family is set to "Sans". This mirrors GTK's behaviour in
216  * gtkstyle.c.
217  *
218  * Some environments will force Pango to specify the family
219  * even if it was not specified in the string describing the font.
220  * Such environments should be left unaffected by this function,
221  * since the font family will be left alone.
222  *
223  * There may be other similar font specification enforcement
224  * that we might add here later.
225  */
226 Pango::FontDescription
227 ARDOUR_UI_UTILS::sanitized_font (std::string const& name)
228 {
229         Pango::FontDescription fd (name);
230
231         if (fd.get_family().empty()) {
232                 fd.set_family ("Sans");
233         }
234
235         return fd;
236 }
237
238 Pango::FontDescription
239 ARDOUR_UI_UTILS::get_font_for_style (string widgetname)
240 {
241         Gtk::Window window (WINDOW_TOPLEVEL);
242         Gtk::Label foobar;
243         Glib::RefPtr<Gtk::Style> style;
244
245         window.add (foobar);
246         foobar.set_name (widgetname);
247         foobar.ensure_style();
248
249         style = foobar.get_style ();
250
251         Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
252
253         PangoFontDescription *pfd = const_cast<PangoFontDescription *> (pango_layout_get_font_description(const_cast<PangoLayout *>(layout->gobj())));
254
255         if (!pfd) {
256
257                 /* layout inherited its font description from a PangoContext */
258
259                 PangoContext* ctxt = (PangoContext*) pango_layout_get_context (const_cast<PangoLayout*>(layout->gobj()));
260                 pfd =  pango_context_get_font_description (ctxt);
261                 return Pango::FontDescription (pfd); /* make a copy */
262         }
263
264         return Pango::FontDescription (pfd); /* make a copy */
265 }
266
267 Gdk::Color
268 ARDOUR_UI_UTILS::gdk_color_from_rgb (uint32_t rgb)
269 {
270         Gdk::Color c;
271         set_color_from_rgb (c, rgb);
272         return c;
273 }
274
275 Gdk::Color
276 ARDOUR_UI_UTILS::gdk_color_from_rgba (uint32_t rgba)
277 {
278         Gdk::Color c;
279         set_color_from_rgb (c, rgba);
280         return c;
281 }
282
283 void
284 ARDOUR_UI_UTILS::set_color_from_rgb (Gdk::Color& c, uint32_t rgb)
285 {
286         /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
287            multiplying by 256.
288         */
289         c.set_rgb ((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
290 }
291
292 void
293 ARDOUR_UI_UTILS::set_color_from_rgba (Gdk::Color& c, uint32_t rgba)
294 {
295         /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
296            multiplying by 256.
297         */
298         c.set_rgb ((rgba >> 24)*256, ((rgba & 0xff0000) >> 16)*256, ((rgba & 0xff00) >> 8)*256);
299 }
300
301 uint32_t
302 ARDOUR_UI_UTILS::gdk_color_to_rgba (Gdk::Color const& c)
303 {
304         /* since alpha value is not available from a Gdk::Color, it is
305            hardcoded as 0xff (aka 255 or 1.0)
306         */
307
308         const uint32_t r = c.get_red_p () * 255.0;
309         const uint32_t g = c.get_green_p () * 255.0;
310         const uint32_t b = c.get_blue_p () * 255.0;
311         const uint32_t a = 0xff;
312
313         return RGBA_TO_UINT (r,g,b,a);
314 }
315
316 bool
317 ARDOUR_UI_UTILS::relay_key_press (GdkEventKey* ev, Gtk::Window* win)
318 {
319         return ARDOUR_UI::instance()->key_event_handler (ev, win);
320 }
321
322 bool
323 ARDOUR_UI_UTILS::emulate_key_event (unsigned int keyval)
324 {
325         GdkDisplay  *display = gtk_widget_get_display (GTK_WIDGET(ARDOUR_UI::instance()->main_window().gobj()));
326         GdkKeymap   *keymap  = gdk_keymap_get_for_display (display);
327         GdkKeymapKey *keymapkey = NULL;
328         gint n_keys;
329
330         if (!gdk_keymap_get_entries_for_keyval(keymap, keyval, &keymapkey, &n_keys)) return false;
331         if (n_keys !=1) { g_free(keymapkey); return false;}
332
333         Gtk::Window& main_window (ARDOUR_UI::instance()->main_window());
334
335         GdkEventKey ev;
336         ev.type = GDK_KEY_PRESS;
337         ev.window = main_window.get_window()->gobj();
338         ev.send_event = FALSE;
339         ev.time = 0;
340         ev.state = 0;
341         ev.keyval = keyval;
342         ev.length = 0;
343         ev.string = const_cast<gchar*> ("");
344         ev.hardware_keycode = keymapkey[0].keycode;
345         ev.group = keymapkey[0].group;
346         g_free(keymapkey);
347
348         relay_key_press (&ev, &main_window);
349         ev.type = GDK_KEY_RELEASE;
350         return relay_key_press(&ev, &main_window);
351 }
352
353 string
354 ARDOUR_UI_UTILS::show_gdk_event_state (int state)
355 {
356         string s;
357         if (state & GDK_SHIFT_MASK) {
358                 s += "+SHIFT";
359         }
360         if (state & GDK_LOCK_MASK) {
361                 s += "+LOCK";
362         }
363         if (state & GDK_CONTROL_MASK) {
364                 s += "+CONTROL";
365         }
366         if (state & GDK_MOD1_MASK) {
367                 s += "+MOD1";
368         }
369         if (state & GDK_MOD2_MASK) {
370                 s += "+MOD2";
371         }
372         if (state & GDK_MOD3_MASK) {
373                 s += "+MOD3";
374         }
375         if (state & GDK_MOD4_MASK) {
376                 s += "+MOD4";
377         }
378         if (state & GDK_MOD5_MASK) {
379                 s += "+MOD5";
380         }
381         if (state & GDK_BUTTON1_MASK) {
382                 s += "+BUTTON1";
383         }
384         if (state & GDK_BUTTON2_MASK) {
385                 s += "+BUTTON2";
386         }
387         if (state & GDK_BUTTON3_MASK) {
388                 s += "+BUTTON3";
389         }
390         if (state & GDK_BUTTON4_MASK) {
391                 s += "+BUTTON4";
392         }
393         if (state & GDK_BUTTON5_MASK) {
394                 s += "+BUTTON5";
395         }
396         if (state & GDK_SUPER_MASK) {
397                 s += "+SUPER";
398         }
399         if (state & GDK_HYPER_MASK) {
400                 s += "+HYPER";
401         }
402         if (state & GDK_META_MASK) {
403                 s += "+META";
404         }
405         if (state & GDK_RELEASE_MASK) {
406                 s += "+RELEASE";
407         }
408
409         return s;
410 }
411
412 Glib::RefPtr<Gdk::Pixbuf>
413 ARDOUR_UI_UTILS::get_xpm (std::string name)
414 {
415         if (!xpm_map[name]) {
416
417                 Searchpath spath(ARDOUR::ardour_data_search_path());
418
419                 spath.add_subdirectory_to_paths("pixmaps");
420
421                 std::string data_file_path;
422
423                 if(!find_file (spath, name, data_file_path)) {
424                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
425                 }
426
427                 try {
428                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path);
429                 } catch(const Glib::Error& e)   {
430                         warning << "Caught Glib::Error: " << e.what() << endmsg;
431                 }
432         }
433
434         return xpm_map[name];
435 }
436
437 void
438 ARDOUR_UI_UTILS::get_color_themes (map<std::string,std::string>& themes)
439 {
440         Searchpath spath(ARDOUR::theme_search_path());
441
442         for (vector<string>::iterator s = spath.begin(); s != spath.end(); ++s) {
443
444                 vector<string> entries;
445
446                 find_files_matching_pattern (entries, *s, string ("*") + UIConfiguration::color_file_suffix);
447
448                 for (vector<string>::iterator e = entries.begin(); e != entries.end(); ++e) {
449
450                         XMLTree tree;
451
452                         tree.read ((*e).c_str());
453                         XMLNode* root = tree.root();
454
455                         if (!root || root->name() != X_("Ardour")) {
456                                 continue;
457                         }
458
459                         XMLProperty const* prop = root->property (X_("theme-name"));
460
461                         if (!prop) {
462                                 continue;
463                         }
464
465                         themes.insert (make_pair (prop->value(), Glib::filename_to_utf8 (basename_nosuffix(*e))));
466                 }
467         }
468 }
469
470 vector<string>
471 ARDOUR_UI_UTILS::get_icon_sets ()
472 {
473         Searchpath spath(ARDOUR::ardour_data_search_path());
474         spath.add_subdirectory_to_paths ("icons");
475         vector<string> r;
476
477         r.push_back (_("default"));
478
479         for (vector<string>::iterator s = spath.begin(); s != spath.end(); ++s) {
480
481                 vector<string> entries;
482
483                 get_paths (entries, *s, false, false);
484
485                 for (vector<string>::iterator e = entries.begin(); e != entries.end(); ++e) {
486                         if (Glib::file_test (*e, Glib::FILE_TEST_IS_DIR)) {
487                                 r.push_back (Glib::filename_to_utf8 (Glib::path_get_basename(*e)));
488                         }
489                 }
490         }
491
492         return r;
493 }
494
495 std::string
496 ARDOUR_UI_UTILS::get_icon_path (const char* cname, string icon_set, bool is_image)
497 {
498         std::string data_file_path;
499         string name = cname;
500
501         if (is_image) {
502                 name += X_(".png");
503         }
504
505         Searchpath spath(ARDOUR::ardour_data_search_path());
506
507         if (!icon_set.empty() && icon_set != _("default")) {
508
509                 /* add "icons/icon_set" but .. not allowed to add both of these at once */
510                 spath.add_subdirectory_to_paths ("icons");
511                 spath.add_subdirectory_to_paths (icon_set);
512
513                 find_file (spath, name, data_file_path);
514         } else {
515                 spath.add_subdirectory_to_paths ("icons");
516                 find_file (spath, name, data_file_path);
517         }
518
519         if (data_file_path.empty()) {
520                 Searchpath rc (ARDOUR::ardour_data_search_path());
521                 rc.add_subdirectory_to_paths ("resources");
522                 find_file (rc, name, data_file_path);
523         }
524
525         if (is_image && data_file_path.empty()) {
526
527                 if (!icon_set.empty() && icon_set != _("default")) {
528                         warning << string_compose (_("icon \"%1\" not found for icon set \"%2\", fallback to default"), cname, icon_set) << endmsg;
529                 }
530
531                 Searchpath def (ARDOUR::ardour_data_search_path());
532                 def.add_subdirectory_to_paths ("icons");
533
534                 if (!find_file (def, name, data_file_path)) {
535                         fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
536                         abort(); /*NOTREACHED*/
537                 }
538         }
539
540         return data_file_path;
541 }
542
543 Glib::RefPtr<Gdk::Pixbuf>
544 ARDOUR_UI_UTILS::get_icon (const char* cname, string icon_set)
545 {
546         Glib::RefPtr<Gdk::Pixbuf> img;
547         try {
548                 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname, icon_set));
549         } catch (const Gdk::PixbufError &e) {
550                 cerr << "Caught PixbufError: " << e.what() << endl;
551         } catch (...) {
552                 error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg;
553         }
554
555         return img;
556 }
557
558 namespace ARDOUR_UI_UTILS {
559 Glib::RefPtr<Gdk::Pixbuf>
560 get_icon (const char* cname)
561 {
562         Glib::RefPtr<Gdk::Pixbuf> img;
563         try {
564                 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
565         } catch (const Gdk::PixbufError &e) {
566                 cerr << "Caught PixbufError: " << e.what() << endl;
567         } catch (...) {
568                 error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg;
569         }
570
571         return img;
572 }
573 }
574
575 string
576 ARDOUR_UI_UTILS::longest (vector<string>& strings)
577 {
578         if (strings.empty()) {
579                 return string ("");
580         }
581
582         vector<string>::iterator longest = strings.begin();
583         string::size_type longest_length = (*longest).length();
584
585         vector<string>::iterator i = longest;
586         ++i;
587
588         while (i != strings.end()) {
589
590                 string::size_type len = (*i).length();
591
592                 if (len > longest_length) {
593                         longest = i;
594                         longest_length = len;
595                 }
596
597                 ++i;
598         }
599
600         return *longest;
601 }
602
603 bool
604 ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (guint keyval)
605 {
606         /* we assume that this does not change over the life of the process
607          */
608
609         static int comma_decimal = -1;
610
611         switch (keyval) {
612         case GDK_period:
613         case GDK_comma:
614                 if (comma_decimal < 0) {
615                         std::lconv* lc = std::localeconv();
616                         if (strchr (lc->decimal_point, ',') != 0) {
617                                 comma_decimal = 1;
618                         } else {
619                                 comma_decimal = 0;
620                         }
621                 }
622                 break;
623         default:
624                 break;
625         }
626
627         switch (keyval) {
628         case GDK_decimalpoint:
629         case GDK_KP_Separator:
630                 return true;
631
632         case GDK_period:
633                 if (comma_decimal) {
634                         return false;
635                 } else {
636                         return true;
637                 }
638                 break;
639         case GDK_comma:
640                 if (comma_decimal) {
641                         return true;
642                 } else {
643                         return false;
644                 }
645                 break;
646         case GDK_minus:
647         case GDK_plus:
648         case GDK_0:
649         case GDK_1:
650         case GDK_2:
651         case GDK_3:
652         case GDK_4:
653         case GDK_5:
654         case GDK_6:
655         case GDK_7:
656         case GDK_8:
657         case GDK_9:
658         case GDK_KP_Add:
659         case GDK_KP_Subtract:
660         case GDK_KP_Decimal:
661         case GDK_KP_0:
662         case GDK_KP_1:
663         case GDK_KP_2:
664         case GDK_KP_3:
665         case GDK_KP_4:
666         case GDK_KP_5:
667         case GDK_KP_6:
668         case GDK_KP_7:
669         case GDK_KP_8:
670         case GDK_KP_9:
671         case GDK_Return:
672         case GDK_BackSpace:
673         case GDK_Delete:
674         case GDK_KP_Enter:
675         case GDK_Home:
676         case GDK_End:
677         case GDK_Left:
678         case GDK_Right:
679                 return true;
680
681         default:
682                 break;
683         }
684
685         return false;
686 }
687
688 void
689 ARDOUR_UI_UTILS::resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
690 {
691         Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
692         Gdk::Rectangle monitor_rect;
693         screen->get_monitor_geometry (0, monitor_rect);
694
695         int const w = std::min (int (monitor_rect.get_width() * 0.8), max_width);
696         int const h = std::min (int (monitor_rect.get_height() * 0.8), max_height);
697
698         window->resize (w, h);
699 }
700
701
702 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
703 string
704 ARDOUR_UI_UTILS::escape_underscores (string const & s)
705 {
706         string o;
707         string::size_type const N = s.length ();
708
709         for (string::size_type i = 0; i < N; ++i) {
710                 if (s[i] == '_') {
711                         o += "__";
712                 } else {
713                         o += s[i];
714                 }
715         }
716
717         return o;
718 }
719
720 Gdk::Color
721 ARDOUR_UI_UTILS::unique_random_color (list<Gdk::Color>& used_colors)
722 {
723         Gdk::Color newcolor;
724
725         while (1) {
726
727                 double h, s, v;
728
729                 h = fmod (random(), 360.0);
730                 s = (random() % 65535) / 65535.0;
731                 v = (random() % 65535) / 65535.0;
732
733                 s = min (0.5, s); /* not too saturated */
734                 v = max (0.9, v);  /* not too bright */
735                 newcolor.set_hsv (h, s, v);
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                                 /* different enough */
752                                 used_colors.push_back (newcolor);
753                                 return newcolor;
754                         }
755                 }
756
757                 /* XXX need throttle here to make sure we don't spin for ever */
758         }
759 }
760
761 string
762 ARDOUR_UI_UTILS::rate_as_string (float r)
763 {
764         char buf[32];
765         if (fmod (r, 1000.0f)) {
766                 snprintf (buf, sizeof (buf), "%.1f kHz", r/1000.0);
767         } else {
768                 snprintf (buf, sizeof (buf), "%.0f kHz", r/1000.0);
769         }
770         return buf;
771 }
772
773 bool
774 ARDOUR_UI_UTILS::windows_overlap (Gtk::Window *a, Gtk::Window *b)
775 {
776
777         if (!a || !b) {
778                 return false;
779         }
780         if (a->get_screen() == b->get_screen()) {
781                 gint ex, ey, ew, eh;
782                 gint mx, my, mw, mh;
783
784                 a->get_position (ex, ey);
785                 a->get_size (ew, eh);
786                 b->get_position (mx, my);
787                 b->get_size (mw, mh);
788
789                 GdkRectangle e;
790                 GdkRectangle m;
791                 GdkRectangle r;
792
793                 e.x = ex;
794                 e.y = ey;
795                 e.width = ew;
796                 e.height = eh;
797
798                 m.x = mx;
799                 m.y = my;
800                 m.width = mw;
801                 m.height = mh;
802
803                 if (gdk_rectangle_intersect (&e, &m, &r)) {
804                         return true;
805                 }
806         }
807         return false;
808 }
809
810 bool
811 ARDOUR_UI_UTILS::overwrite_file_dialog (Gtk::Window& parent, string title, string text)
812 {
813         ArdourDialog dialog (parent, title, true);
814         Label label (text);
815
816         dialog.get_vbox()->pack_start (label, true, true);
817         dialog.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
818         dialog.add_button (_("Overwrite"), Gtk::RESPONSE_ACCEPT);
819         dialog.show_all ();
820
821         switch (dialog.run()) {
822         case RESPONSE_ACCEPT:
823                 return true;
824         case RESPONSE_CANCEL:
825         default:
826                 return false;
827         }
828 }
829
830 bool
831 ARDOUR_UI_UTILS::running_from_source_tree ()
832 {
833         gchar const *x = g_getenv ("ARDOUR_THEMES_PATH");
834         return x && (string (x).find ("gtk2_ardour") != string::npos);
835 }