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