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