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