group Product/App specific resources
[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/file_utils.h"
40
41 #include <gtkmm2ext/utils.h>
42
43 #include "ardour/filesystem_paths.h"
44
45 #include "canvas/item.h"
46 #include "canvas/utils.h"
47
48 #include "debug.h"
49 #include "public_editor.h"
50 #include "keyboard.h"
51 #include "utils.h"
52 #include "i18n.h"
53 #include "rgb_macros.h"
54 #include "gui_thread.h"
55 #include "ui_config.h"
56 #include "ardour_dialog.h"
57 #include "ardour_ui.h"
58
59 using namespace std;
60 using namespace Gtk;
61 using namespace Glib;
62 using namespace PBD;
63 using Gtkmm2ext::Keyboard;
64
65 namespace ARDOUR_UI_UTILS {
66         sigc::signal<void>  DPIReset;
67 }
68
69 #ifdef PLATFORM_WINDOWS
70 #define random() rand()
71 #endif
72
73
74 /** Add an element to a menu, settings its sensitivity.
75  * @param m Menu to add to.
76  * @param e Element to add.
77  * @param s true to make sensitive, false to make insensitive
78  */
79 void
80 ARDOUR_UI_UTILS::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
81 {
82         m.push_back (e);
83         if (!s) {
84                 m.back().set_sensitive (false);
85         }
86 }
87
88
89 gint
90 ARDOUR_UI_UTILS::just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
91 {
92         win->hide ();
93         return 0;
94 }
95
96 /* xpm2rgb copied from nixieclock, which bore the legend:
97
98     nixieclock - a nixie desktop timepiece
99     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
100
101     and was released under the GPL.
102 */
103
104 unsigned char*
105 ARDOUR_UI_UTILS::xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
106 {
107         static long vals[256], val;
108         uint32_t t, x, y, colors, cpp;
109         unsigned char c;
110         unsigned char *savergb, *rgb;
111
112         // PARSE HEADER
113
114         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
115                 error << string_compose (_("bad XPM header %1"), xpm[0])
116                       << endmsg;
117                 return 0;
118         }
119
120         savergb = rgb = (unsigned char*) malloc (h * w * 3);
121
122         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
123         for (t = 0; t < colors; ++t) {
124                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
125                 vals[c] = val;
126         }
127
128         // COLORMAP -> RGB CONVERSION
129         //    Get low 3 bytes from vals[]
130         //
131
132         const char *p;
133         for (y = h-1; y > 0; --y) {
134
135                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
136                         val = vals[(int)*p++];
137                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
138                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
139                         *(rgb+0) = val & 0xff;             // 0:R
140                 }
141         }
142
143         return (savergb);
144 }
145
146 unsigned char*
147 ARDOUR_UI_UTILS::xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
148 {
149         static long vals[256], val;
150         uint32_t t, x, y, colors, cpp;
151         unsigned char c;
152         unsigned char *savergb, *rgb;
153         char transparent;
154
155         // PARSE HEADER
156
157         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
158                 error << string_compose (_("bad XPM header %1"), xpm[0])
159                       << endmsg;
160                 return 0;
161         }
162
163         savergb = rgb = (unsigned char*) malloc (h * w * 4);
164
165         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
166
167         if (strstr (xpm[1], "None")) {
168                 sscanf (xpm[1], "%c", &transparent);
169                 t = 1;
170         } else {
171                 transparent = 0;
172                 t = 0;
173         }
174
175         for (; t < colors; ++t) {
176                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
177                 vals[c] = val;
178         }
179
180         // COLORMAP -> RGB CONVERSION
181         //    Get low 3 bytes from vals[]
182         //
183
184         const char *p;
185         for (y = h-1; y > 0; --y) {
186
187                 char alpha;
188
189                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
190
191                         if (transparent && (*p++ == transparent)) {
192                                 alpha = 0;
193                                 val = 0;
194                         } else {
195                                 alpha = 255;
196                                 val = vals[(int)*p];
197                         }
198
199                         *(rgb+3) = alpha;                  // 3: alpha
200                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
201                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
202                         *(rgb+0) = val & 0xff;             // 0:R
203                 }
204         }
205
206         return (savergb);
207 }
208
209 /** Returns a Pango::FontDescription given a string describing the font.
210  *
211  * If the returned FontDescription does not specify a family, then
212  * the family is set to "Sans". This mirrors GTK's behaviour in
213  * gtkstyle.c.
214  *
215  * Some environments will force Pango to specify the family
216  * even if it was not specified in the string describing the font.
217  * Such environments should be left unaffected by this function,
218  * since the font family will be left alone.
219  *
220  * There may be other similar font specification enforcement
221  * that we might add here later.
222  */
223 Pango::FontDescription
224 ARDOUR_UI_UTILS::sanitized_font (std::string const& name)
225 {
226         Pango::FontDescription fd (name);
227
228         if (fd.get_family().empty()) {
229                 fd.set_family ("Sans");
230         }
231
232         return fd;
233 }
234
235 Pango::FontDescription
236 ARDOUR_UI_UTILS::get_font_for_style (string widgetname)
237 {
238         Gtk::Window window (WINDOW_TOPLEVEL);
239         Gtk::Label foobar;
240         Glib::RefPtr<Gtk::Style> style;
241
242         window.add (foobar);
243         foobar.set_name (widgetname);
244         foobar.ensure_style();
245
246         style = foobar.get_style ();
247
248         Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
249
250         PangoFontDescription *pfd = const_cast<PangoFontDescription *> (pango_layout_get_font_description(const_cast<PangoLayout *>(layout->gobj())));
251
252         if (!pfd) {
253
254                 /* layout inherited its font description from a PangoContext */
255
256                 PangoContext* ctxt = (PangoContext*) pango_layout_get_context (const_cast<PangoLayout*>(layout->gobj()));
257                 pfd =  pango_context_get_font_description (ctxt);
258                 return Pango::FontDescription (pfd); /* make a copy */
259         }
260
261         return Pango::FontDescription (pfd); /* make a copy */
262 }
263
264 void
265 ARDOUR_UI_UTILS::set_color_from_rgb (Gdk::Color& c, uint32_t rgb)
266 {
267         /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
268            multiplying by 256.
269         */
270         c.set_rgb ((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
271 }
272
273 void
274 ARDOUR_UI_UTILS::set_color_from_rgba (Gdk::Color& c, uint32_t rgba)
275 {
276         /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
277            multiplying by 256.
278         */
279         c.set_rgb ((rgba >> 24)*256, ((rgba & 0xff0000) >> 16)*256, ((rgba & 0xff00) >> 8)*256);
280 }
281
282 uint32_t
283 ARDOUR_UI_UTILS::gdk_color_to_rgba (Gdk::Color const& c)
284 {
285         /* since alpha value is not available from a Gdk::Color, it is
286            hardcoded as 0xff (aka 255 or 1.0)
287         */
288
289         const uint32_t r = c.get_red_p () * 255.0;
290         const uint32_t g = c.get_green_p () * 255.0;
291         const uint32_t b = c.get_blue_p () * 255.0;
292         const uint32_t a = 0xff;
293
294         return RGBA_TO_UINT (r,g,b,a);
295 }
296
297
298 bool
299 ARDOUR_UI_UTILS::relay_key_press (GdkEventKey* ev, Gtk::Window* win)
300 {
301         return ARDOUR_UI::instance()->key_event_handler (ev, win);
302 }
303
304 bool
305 ARDOUR_UI_UTILS::emulate_key_event (unsigned int keyval)
306 {
307         GdkDisplay  *display = gtk_widget_get_display (GTK_WIDGET(ARDOUR_UI::instance()->main_window().gobj()));
308         GdkKeymap   *keymap  = gdk_keymap_get_for_display (display);
309         GdkKeymapKey *keymapkey = NULL;
310         gint n_keys;
311
312         if (!gdk_keymap_get_entries_for_keyval(keymap, keyval, &keymapkey, &n_keys)) return false;
313         if (n_keys !=1) { g_free(keymapkey); return false;}
314
315         Gtk::Window& main_window (ARDOUR_UI::instance()->main_window());
316
317         GdkEventKey ev;
318         ev.type = GDK_KEY_PRESS;
319         ev.window = main_window.get_window()->gobj();
320         ev.send_event = FALSE;
321         ev.time = 0;
322         ev.state = 0;
323         ev.keyval = keyval;
324         ev.length = 0;
325         ev.string = const_cast<gchar*> ("");
326         ev.hardware_keycode = keymapkey[0].keycode;
327         ev.group = keymapkey[0].group;
328         g_free(keymapkey);
329
330         relay_key_press (&ev, &main_window);
331         ev.type = GDK_KEY_RELEASE;
332         return relay_key_press(&ev, &main_window);
333 }
334
335 string
336 ARDOUR_UI_UTILS::show_gdk_event_state (int state)
337 {
338         string s;
339         if (state & GDK_SHIFT_MASK) {
340                 s += "+SHIFT";
341         }
342         if (state & GDK_LOCK_MASK) {
343                 s += "+LOCK";
344         }
345         if (state & GDK_CONTROL_MASK) {
346                 s += "+CONTROL";
347         }
348         if (state & GDK_MOD1_MASK) {
349                 s += "+MOD1";
350         }
351         if (state & GDK_MOD2_MASK) {
352                 s += "+MOD2";
353         }
354         if (state & GDK_MOD3_MASK) {
355                 s += "+MOD3";
356         }
357         if (state & GDK_MOD4_MASK) {
358                 s += "+MOD4";
359         }
360         if (state & GDK_MOD5_MASK) {
361                 s += "+MOD5";
362         }
363         if (state & GDK_BUTTON1_MASK) {
364                 s += "+BUTTON1";
365         }
366         if (state & GDK_BUTTON2_MASK) {
367                 s += "+BUTTON2";
368         }
369         if (state & GDK_BUTTON3_MASK) {
370                 s += "+BUTTON3";
371         }
372         if (state & GDK_BUTTON4_MASK) {
373                 s += "+BUTTON4";
374         }
375         if (state & GDK_BUTTON5_MASK) {
376                 s += "+BUTTON5";
377         }
378         if (state & GDK_SUPER_MASK) {
379                 s += "+SUPER";
380         }
381         if (state & GDK_HYPER_MASK) {
382                 s += "+HYPER";
383         }
384         if (state & GDK_META_MASK) {
385                 s += "+META";
386         }
387         if (state & GDK_RELEASE_MASK) {
388                 s += "+RELEASE";
389         }
390
391         return s;
392 }
393
394 Glib::RefPtr<Gdk::Pixbuf>
395 ARDOUR_UI_UTILS::get_xpm (std::string name)
396 {
397         if (!xpm_map[name]) {
398
399                 Searchpath spath(ARDOUR::ardour_data_search_path());
400
401                 spath.add_subdirectory_to_paths("pixmaps");
402
403                 std::string data_file_path;
404
405                 if(!find_file (spath, name, data_file_path)) {
406                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
407                 }
408
409                 try {
410                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path);
411                 } catch(const Glib::Error& e)   {
412                         warning << "Caught Glib::Error: " << e.what() << endmsg;
413                 }
414         }
415
416         return xpm_map[name];
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 /** Replace < and > with &lt; and &gt; respectively to make < > display correctly in markup strings */
670 string
671 ARDOUR_UI_UTILS::escape_angled_brackets (string const & s)
672 {
673         string o = s;
674         boost::replace_all (o, "<", "&lt;");
675         boost::replace_all (o, ">", "&gt;");
676         return o;
677 }
678
679 Gdk::Color
680 ARDOUR_UI_UTILS::unique_random_color (list<Gdk::Color>& used_colors)
681 {
682         Gdk::Color newcolor;
683
684         while (1) {
685
686                 double h, s, v;
687
688                 h = fmod (random(), 360.0);
689                 s = (random() % 65535) / 65535.0;
690                 v = (random() % 65535) / 65535.0;
691
692                 s = min (0.5, s); /* not too saturated */
693                 v = max (0.9, v);  /* not too bright */
694                 newcolor.set_hsv (h, s, v);
695
696                 if (used_colors.size() == 0) {
697                         used_colors.push_back (newcolor);
698                         return newcolor;
699                 }
700
701                 for (list<Gdk::Color>::iterator i = used_colors.begin(); i != used_colors.end(); ++i) {
702                   Gdk::Color c = *i;
703                         float rdelta, bdelta, gdelta;
704
705                         rdelta = newcolor.get_red() - c.get_red();
706                         bdelta = newcolor.get_blue() - c.get_blue();
707                         gdelta = newcolor.get_green() - c.get_green();
708
709                         if (sqrt (rdelta*rdelta + bdelta*bdelta + gdelta*gdelta) > 25.0) {
710                                 /* different enough */
711                                 used_colors.push_back (newcolor);
712                                 return newcolor;
713                         }
714                 }
715
716                 /* XXX need throttle here to make sure we don't spin for ever */
717         }
718 }
719
720 string
721 ARDOUR_UI_UTILS::rate_as_string (float r)
722 {
723         char buf[32];
724         if (fmod (r, 1000.0f)) {
725                 snprintf (buf, sizeof (buf), "%.1f kHz", r/1000.0);
726         } else {
727                 snprintf (buf, sizeof (buf), "%.0f kHz", r/1000.0);
728         }
729         return buf;
730 }
731
732 bool
733 ARDOUR_UI_UTILS::windows_overlap (Gtk::Window *a, Gtk::Window *b)
734 {
735
736         if (!a || !b) {
737                 return false;
738         }
739         if (a->get_screen() == b->get_screen()) {
740                 gint ex, ey, ew, eh;
741                 gint mx, my, mw, mh;
742
743                 a->get_position (ex, ey);
744                 a->get_size (ew, eh);
745                 b->get_position (mx, my);
746                 b->get_size (mw, mh);
747
748                 GdkRectangle e;
749                 GdkRectangle m;
750                 GdkRectangle r;
751
752                 e.x = ex;
753                 e.y = ey;
754                 e.width = ew;
755                 e.height = eh;
756
757                 m.x = mx;
758                 m.y = my;
759                 m.width = mw;
760                 m.height = mh;
761
762                 if (gdk_rectangle_intersect (&e, &m, &r)) {
763                         return true;
764                 }
765         }
766         return false;
767 }
768
769 bool
770 ARDOUR_UI_UTILS::overwrite_file_dialog (Gtk::Window& parent, string title, string text)
771 {
772         ArdourDialog dialog (parent, title, true);
773         Label label (text);
774
775         dialog.get_vbox()->pack_start (label, true, true);
776         dialog.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
777         dialog.add_button (_("Overwrite"), Gtk::RESPONSE_ACCEPT);
778         dialog.show_all ();
779
780         switch (dialog.run()) {
781         case RESPONSE_ACCEPT:
782                 return true;
783         case RESPONSE_CANCEL:
784         default:
785                 return false;
786         }
787 }