enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / gtkmm2ext / gtk_ui.cc
1 /*
2     Copyright (C) 1999-2005 Paul Barton-Davis
3
4     This program is free software; you can 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     $Id$
19 */
20
21 #include <cmath>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <unistd.h>
25 #include <cerrno>
26 #include <climits>
27 #include <cctype>
28
29 #include <gtkmm.h>
30
31 #include "pbd/error.h"
32 #include "pbd/touchable.h"
33 #include "pbd/failed_constructor.h"
34 #include "pbd/pthread_utils.h"
35 #include "pbd/replace_all.h"
36
37 #include "gtkmm2ext/application.h"
38 #include "gtkmm2ext/bindings.h"
39 #include "gtkmm2ext/gtk_ui.h"
40 #include "gtkmm2ext/textviewer.h"
41 #include "gtkmm2ext/popup.h"
42 #include "gtkmm2ext/utils.h"
43 #include "gtkmm2ext/window_title.h"
44 #include "gtkmm2ext/actions.h"
45 #include "gtkmm2ext/activatable.h"
46 #include "gtkmm2ext/actions.h"
47 #include "gtkmm2ext/gui_thread.h"
48
49 #include "pbd/i18n.h"
50
51 using namespace Gtkmm2ext;
52 using namespace Gtk;
53 using namespace Glib;
54 using namespace PBD;
55 using std::map;
56
57 UI*   UI::theGtkUI = 0;
58 float UI::ui_scale = 1.0;
59
60 BaseUI::RequestType Gtkmm2ext::NullMessage = BaseUI::new_request_type();
61 BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
62 BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type();
63 BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type();
64 BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type();
65 BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type();
66 BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
67
68 #include "pbd/abstract_ui.cc"  /* instantiate the template */
69
70 template class AbstractUI<Gtkmm2ext::UIRequest>;
71
72 UI::UI (string application_name, string thread_name, int *argc, char ***argv)
73         : AbstractUI<UIRequest> (thread_name)
74         , _receiver (*this)
75         , global_bindings (0)
76         , errors (0)
77 {
78         theMain = new Main (argc, argv);
79
80         pthread_set_name ("gui");
81
82         _active = false;
83
84         if (!theGtkUI) {
85                 theGtkUI = this;
86         } else {
87                 fatal << "duplicate UI requested" << endmsg;
88                 abort(); /* NOTREACHED */
89         }
90
91         /* the GUI event loop runs in the main thread of the app,
92            which is assumed to have called this.
93         */
94
95         run_loop_thread = Threads::Thread::self();
96
97         /* store "this" as the UI-for-thread of this thread, same argument
98            as for previous line.
99         */
100
101         set_event_loop_for_thread (this);
102
103         /* we will be receiving requests */
104
105         EventLoop::register_request_buffer_factory ("gui", request_buffer_factory);
106
107         /* attach our request source to the default main context */
108
109         attach_request_source ();
110
111         errors = new TextViewer (800,600);
112         errors->text().set_editable (false);
113         errors->text().set_name ("ErrorText");
114         errors->signal_unmap().connect (sigc::bind (sigc::ptr_fun (&ActionManager::uncheck_toggleaction), X_("<Actions>/Editor/toggle-log-window")));
115
116         Glib::set_application_name (application_name);
117
118         WindowTitle title(Glib::get_application_name());
119         title += _("Log");
120         errors->set_title (title.get_string());
121
122         errors->dismiss_button().set_name ("ErrorLogCloseButton");
123         errors->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
124         errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
125
126         //load_rcfile (rcfile);
127
128         /* instantiate the Application singleton */
129
130         Application::instance();
131 }
132
133 UI::~UI ()
134 {
135         _receiver.hangup ();
136         delete (errors);
137 }
138
139 bool
140 UI::caller_is_ui_thread ()
141 {
142         return Threads::Thread::self() == run_loop_thread;
143 }
144
145 int
146 UI::load_rcfile (string path, bool themechange)
147 {
148         /* Yes, pointers to Glib::RefPtr.  If these are not kept around,
149          * a segfault somewhere deep in the wonderfully robust glib will result.
150          * This does not occur if wiget.get_style is used instead of rc.get_style below,
151          * except that doesn't actually work...
152          */
153
154         static Glib::RefPtr<Style>* fatal_style   = 0;
155         static Glib::RefPtr<Style>* error_style   = 0;
156         static Glib::RefPtr<Style>* warning_style = 0;
157         static Glib::RefPtr<Style>* info_style    = 0;
158
159         if (path.length() == 0) {
160                 return -1;
161         }
162
163         if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
164                 error << "UI: couldn't find rc file \""
165                       << path
166                       << '"'
167                       << endmsg;
168                 return -1;
169         }
170
171         RC rc (path.c_str());
172         //this is buggy in gtkmm for some reason, so use C
173         //RC::reset_styles (Gtk::Settings::get_default());
174         gtk_rc_reset_styles (gtk_settings_get_default());
175
176         theme_changed.emit();
177
178         if (themechange) {
179                 return 0; //Don't continue on every time there is a theme change
180         }
181
182         /* have to pack widgets into a toplevel window so that styles will stick */
183
184         Window temp_window (WINDOW_TOPLEVEL);
185         temp_window.ensure_style ();
186
187         HBox box;
188         Label fatal_widget;
189         Label error_widget;
190         Label warning_widget;
191         Label info_widget;
192         RefPtr<Gtk::Style> style;
193         RefPtr<TextBuffer> buffer (errors->text().get_buffer());
194
195         box.pack_start (fatal_widget);
196         box.pack_start (error_widget);
197         box.pack_start (warning_widget);
198         box.pack_start (info_widget);
199
200         error_ptag = buffer->create_tag();
201         error_mtag = buffer->create_tag();
202         fatal_ptag = buffer->create_tag();
203         fatal_mtag = buffer->create_tag();
204         warning_ptag = buffer->create_tag();
205         warning_mtag = buffer->create_tag();
206         info_ptag = buffer->create_tag();
207         info_mtag = buffer->create_tag();
208
209         fatal_widget.set_name ("FatalMessage");
210         delete fatal_style;
211
212         /* This next line and the similar ones below are sketchily
213          * guessed to fix #2885.  I think maybe that problems occur
214          * because with gtk_rc_get_style (to quote its docs) "no
215          * refcount is added to the returned style".  So I've switched
216          * this to use Glib::wrap with take_copy == true, which requires
217          * all the nasty casts and calls to plain-old-C GTK.
218          *
219          * At worst I think this causes a memory leak; at least it appears
220          * to fix the bug.
221          *
222          * I could be wrong about any or all of the above.
223          */
224         fatal_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (fatal_widget.gobj())), true));
225
226         fatal_ptag->property_font_desc().set_value((*fatal_style)->get_font());
227         fatal_ptag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_ACTIVE));
228         fatal_ptag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_ACTIVE));
229         fatal_mtag->property_font_desc().set_value((*fatal_style)->get_font());
230         fatal_mtag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_NORMAL));
231         fatal_mtag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_NORMAL));
232
233         error_widget.set_name ("ErrorMessage");
234         delete error_style;
235         error_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (error_widget.gobj())), true));
236
237         error_ptag->property_font_desc().set_value((*error_style)->get_font());
238         error_ptag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_ACTIVE));
239         error_ptag->property_background_gdk().set_value((*error_style)->get_bg(STATE_ACTIVE));
240         error_mtag->property_font_desc().set_value((*error_style)->get_font());
241         error_mtag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_NORMAL));
242         error_mtag->property_background_gdk().set_value((*error_style)->get_bg(STATE_NORMAL));
243
244         warning_widget.set_name ("WarningMessage");
245         delete warning_style;
246         warning_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (warning_widget.gobj())), true));
247
248         warning_ptag->property_font_desc().set_value((*warning_style)->get_font());
249         warning_ptag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_ACTIVE));
250         warning_ptag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_ACTIVE));
251         warning_mtag->property_font_desc().set_value((*warning_style)->get_font());
252         warning_mtag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_NORMAL));
253         warning_mtag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_NORMAL));
254
255         info_widget.set_name ("InfoMessage");
256         delete info_style;
257         info_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (info_widget.gobj())), true));
258
259         info_ptag->property_font_desc().set_value((*info_style)->get_font());
260         info_ptag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_ACTIVE));
261         info_ptag->property_background_gdk().set_value((*info_style)->get_bg(STATE_ACTIVE));
262         info_mtag->property_font_desc().set_value((*info_style)->get_font());
263         info_mtag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_NORMAL));
264         info_mtag->property_background_gdk().set_value((*info_style)->get_bg(STATE_NORMAL));
265
266         return 0;
267 }
268
269 void
270 UI::run (Receiver &old_receiver)
271 {
272         _receiver.listen_to (error);
273         _receiver.listen_to (info);
274         _receiver.listen_to (warning);
275         _receiver.listen_to (fatal);
276
277         /* stop the old receiver (text/console) once we hit the first idle */
278
279         Glib::signal_idle().connect (bind_return (mem_fun (old_receiver, &Receiver::hangup), false));
280
281         if (starting ()) {
282                 return;
283         }
284
285         _active = true;
286         theMain->run ();
287         _active = false;
288
289         return;
290 }
291
292 bool
293 UI::running ()
294 {
295         return _active;
296 }
297
298 void
299 UI::quit ()
300 {
301         UIRequest *req = get_request (Quit);
302
303         if (req == 0) {
304                 return;
305         }
306
307         send_request (req);
308 }
309
310 static bool idle_quit ()
311 {
312         Main::quit ();
313         return true;
314 }
315
316 void
317 UI::do_quit ()
318 {
319         if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
320                 Main::quit ();
321         } else {
322                 Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
323         }
324 }
325
326 void
327 UI::touch_display (Touchable *display)
328 {
329         UIRequest *req = get_request (TouchDisplay);
330
331         if (req == 0) {
332                 return;
333         }
334
335         req->display = display;
336
337         send_request (req);
338 }
339
340 void
341 UI::set_tip (Widget &w, const gchar *tip)
342 {
343         set_tip(&w, tip, "");
344 }
345
346 void
347 UI::set_tip (Widget &w, const std::string& tip)
348 {
349         set_tip(&w, tip.c_str(), "");
350 }
351
352 void
353 UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
354 {
355         UIRequest *req = get_request (SetTip);
356
357         std::string msg(tip);
358
359         Glib::RefPtr<Gtk::Action> action = w->get_action();
360
361         if (!action) {
362                 Gtkmm2ext::Activatable* activatable;
363                 if ((activatable = dynamic_cast<Gtkmm2ext::Activatable*>(w))) {
364                         action = activatable->get_related_action();
365                 }
366         }
367
368         if (action) {
369                 Bindings* bindings = (Bindings*) w->get_data ("ardour-bindings");
370                 if (!bindings) {
371                         Gtk::Window* win = (Gtk::Window*) w->get_toplevel();
372                         if (win) {
373                                 bindings = (Bindings*) win->get_data ("ardour-bindings");
374                         }
375                 }
376
377                 if (!bindings) {
378                         bindings = global_bindings;
379                 }
380
381                 if (bindings) {
382                         Bindings::Operation op;
383                         KeyboardKey kb = bindings->get_binding_for_action (action, op);
384                         string shortcut = kb.display_label ();
385                         if (!shortcut.empty()) {
386                                 replace_all (shortcut, "<", "");
387                                 replace_all (shortcut, ">", "-");
388                                 msg.append(_("\n\nShortcut: ")).append (shortcut);
389                         }
390                 }
391         }
392
393         if (req == 0) {
394                 return;
395         }
396
397
398         req->widget = w;
399         req->msg = msg.c_str();
400         req->msg2 = hlp;
401
402         send_request (req);
403 }
404
405 void
406 UI::set_state (Widget *w, StateType state)
407 {
408         UIRequest *req = get_request (StateChange);
409
410         if (req == 0) {
411                 return;
412         }
413
414         req->new_state = state;
415         req->widget = w;
416
417         send_request (req);
418 }
419
420 void
421 UI::idle_add (int (*func)(void *), void *arg)
422 {
423         UIRequest *req = get_request (AddIdle);
424
425         if (req == 0) {
426                 return;
427         }
428
429         req->function = func;
430         req->arg = arg;
431
432         send_request (req);
433 }
434
435 /* END abstract_ui interfaces */
436
437 /** Create a PBD::EventLoop::InvalidationRecord and attach a callback
438  *  to a given sigc::trackable so that PBD::EventLoop::invalidate_request
439  *  is called when that trackable is destroyed.
440  */
441 PBD::EventLoop::InvalidationRecord*
442 __invalidator (sigc::trackable& trackable, const char* file, int line)
443 {
444         PBD::EventLoop::InvalidationRecord* ir = new PBD::EventLoop::InvalidationRecord;
445
446         ir->file = file;
447         ir->line = line;
448
449         trackable.add_destroy_notify_callback (ir, PBD::EventLoop::invalidate_request);
450
451         return ir;
452 }
453
454 void
455 UI::do_request (UIRequest* req)
456 {
457         if (req->type == ErrorMessage) {
458
459                 process_error_message (req->chn, req->msg);
460                 free (const_cast<char*>(req->msg)); /* it was strdup'ed */
461                 req->msg = 0; /* don't free it again in the destructor */
462
463         } else if (req->type == Quit) {
464
465                 do_quit ();
466
467         } else if (req->type == CallSlot) {
468 #ifndef NDEBUG
469                 if (getenv ("DEBUG_THREADED_SIGNALS")) {
470                         cerr << "call slot for " << event_loop_name() << endl;
471                 }
472 #endif
473                 req->the_slot ();
474
475         } else if (req->type == TouchDisplay) {
476
477                 req->display->touch ();
478                 if (req->display->delete_after_touch()) {
479                         delete req->display;
480                 }
481
482         } else if (req->type == StateChange) {
483
484                 req->widget->set_state (req->new_state);
485
486         } else if (req->type == SetTip) {
487
488                 gtk_widget_set_tooltip_markup (req->widget->gobj(), req->msg);
489
490         } else {
491
492                 error << "GtkUI: unknown request type "
493                       << (int) req->type
494                       << endmsg;
495         }
496 }
497
498 /*======================================================================
499   Error Display
500   ======================================================================*/
501
502 void
503 UI::dump_errors (std::ostream& ostr)
504 {
505         Glib::Threads::Mutex::Lock lm (error_lock);
506         ostr << endl << X_("Errors/Messages:") << endl;
507         for (list<string>::const_iterator i = error_stack.begin(); i != error_stack.end(); ++i) {
508                 ostr << *i << endl;
509         }
510         ostr << endl;
511 }
512
513 void
514 UI::receive (Transmitter::Channel chn, const char *str)
515 {
516         {
517                 Glib::Threads::Mutex::Lock lm (error_lock);
518                 switch (chn) {
519                 case Transmitter::Fatal:
520                         error_stack.push_back (string (X_("FATAL: ")) + str);
521                         break;
522                 case Transmitter::Error:
523                         error_stack.push_back (string (X_("ERROR: ")) + str);
524                         break;
525                 case Transmitter::Warning:
526                         error_stack.push_back (string (X_("WARNING: ")) + str);
527                         break;
528                 case Transmitter::Info:
529                         error_stack.push_back (string (X_("INFO: ")) + str);
530                         break;
531                 case Transmitter::Throw:
532                         error_stack.push_back (string (X_("THROW: ")) + str);
533                         break;
534                 }
535         }
536
537         if (caller_is_ui_thread()) {
538                 process_error_message (chn, str);
539         } else {
540                 UIRequest* req = get_request (ErrorMessage);
541
542                 if (req == 0) {
543                         return;
544                 }
545
546                 req->chn = chn;
547                 req->msg = strdup (str);
548
549                 send_request (req);
550         }
551 }
552
553 #define OLD_STYLE_ERRORS 1
554
555 void
556 UI::process_error_message (Transmitter::Channel chn, const char *str)
557 {
558         RefPtr<Style> style;
559         RefPtr<TextBuffer::Tag> ptag;
560         RefPtr<TextBuffer::Tag> mtag;
561         const char *prefix;
562         size_t prefix_len;
563         bool fatal_received = false;
564 #ifndef OLD_STYLE_ERRORS
565         PopUp* popup = new PopUp (WIN_POS_CENTER, 0, true);
566 #endif
567
568         switch (chn) {
569         case Transmitter::Fatal:
570                 prefix = "[FATAL]: ";
571                 ptag = fatal_ptag;
572                 mtag = fatal_mtag;
573                 prefix_len = 9;
574                 fatal_received = true;
575                 break;
576         case Transmitter::Error:
577 #if OLD_STYLE_ERRORS
578                 prefix = "[ERROR]: ";
579                 ptag = error_ptag;
580                 mtag = error_mtag;
581                 prefix_len = 9;
582 #else
583                 popup->set_name ("ErrorMessage");
584                 popup->set_text (str);
585                 popup->touch ();
586                 return;
587 #endif
588                 break;
589         case Transmitter::Info:
590 #if OLD_STYLE_ERRORS
591                 prefix = "[INFO]: ";
592                 ptag = info_ptag;
593                 mtag = info_mtag;
594                 prefix_len = 8;
595 #else
596                 popup->set_name ("InfoMessage");
597                 popup->set_text (str);
598                 popup->touch ();
599                 return;
600 #endif
601
602                 break;
603         case Transmitter::Warning:
604 #if OLD_STYLE_ERRORS
605                 prefix = "[WARNING]: ";
606                 ptag = warning_ptag;
607                 mtag = warning_mtag;
608                 prefix_len = 11;
609 #else
610                 popup->set_name ("WarningMessage");
611                 popup->set_text (str);
612                 popup->touch ();
613                 return;
614 #endif
615                 break;
616         default:
617                 /* no choice but to use text/console output here */
618                 cerr << "programmer error in UI::check_error_messages (channel = " << chn << ")\n";
619                 ::exit (1);
620         }
621
622         errors->text().get_buffer()->begin_user_action();
623
624         if (fatal_received) {
625                 handle_fatal (str);
626         } else {
627
628                 if (!ptag || !mtag) {
629                         /* oops, message sent before we set up tags - don't crash */
630                         cerr << prefix << str << endl;
631                 } else {
632                         display_message (prefix, prefix_len, ptag, mtag, str);
633                 }
634         }
635
636         errors->text().get_buffer()->end_user_action();
637 }
638
639 void
640 UI::show_errors ()
641 {
642         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
643         if (!act) {
644                 return;
645         }
646
647         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
648         if (tact) {
649                 tact->set_active ();
650         }
651 }
652
653 void
654 UI::toggle_errors ()
655 {
656         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
657         if (!act) {
658                 return;
659         }
660
661         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
662
663         if (tact->get_active()) {
664                 errors->set_position (WIN_POS_MOUSE);
665                 errors->show ();
666         } else {
667                 errors->hide ();
668         }
669 }
670
671 void
672 UI::display_message (const char *prefix, gint /*prefix_len*/, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg)
673 {
674         RefPtr<TextBuffer> buffer (errors->text().get_buffer());
675
676         buffer->insert_with_tag(buffer->end(), prefix, ptag);
677         buffer->insert_with_tag(buffer->end(), msg, mtag);
678         buffer->insert_with_tag(buffer->end(), "\n", mtag);
679
680         errors->scroll_to_bottom ();
681 }
682
683 void
684 UI::handle_fatal (const char *message)
685 {
686         Dialog win;
687         Label label (message);
688         Button quit (_("Press To Exit"));
689         HBox hpacker;
690
691         win.set_default_size (400, 100);
692
693         WindowTitle title(Glib::get_application_name());
694         title += ": Fatal Error";
695         win.set_title (title.get_string());
696
697         win.set_position (WIN_POS_MOUSE);
698         win.set_border_width (12);
699
700         win.get_vbox()->pack_start (label, true, true);
701         hpacker.pack_start (quit, true, false);
702         win.get_vbox()->pack_start (hpacker, false, false);
703
704         quit.signal_clicked().connect(mem_fun(*this,&UI::quit));
705
706         win.show_all ();
707         win.set_modal (true);
708
709         theMain->run ();
710
711         _exit (1);
712 }
713
714 void
715 UI::popup_error (const string& text)
716 {
717         if (!caller_is_ui_thread()) {
718                 error << "non-UI threads can't use UI::popup_error"
719                       << endmsg;
720                 return;
721         }
722
723         MessageDialog msg (text);
724         msg.set_title (string_compose (_("I'm sorry %1, I can't do that"), g_get_user_name()));
725         msg.set_wmclass (X_("error"), Glib::get_application_name());
726         msg.set_position (WIN_POS_MOUSE);
727         msg.run ();
728 }
729
730 void
731 UI::flush_pending ()
732 {
733         if (!caller_is_ui_thread()) {
734                 error << "non-UI threads cannot call UI::flush_pending()"
735                       << endmsg;
736                 return;
737         }
738
739         gtk_main_iteration();
740
741         while (gtk_events_pending()) {
742                 gtk_main_iteration();
743         }
744 }
745
746 bool
747 UI::just_hide_it (GdkEventAny* /*ev*/, Window *win)
748 {
749         win->hide ();
750         return true;
751 }
752
753 Gdk::Color
754 UI::get_color (const string& prompt, bool& picked, const Gdk::Color* initial)
755 {
756         Gdk::Color color;
757
758         ColorSelectionDialog color_dialog (prompt);
759
760         color_dialog.set_modal (true);
761         color_dialog.get_cancel_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), false));
762         color_dialog.get_ok_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), true));
763         color_dialog.signal_delete_event().connect (mem_fun (*this, &UI::color_selection_deleted));
764
765         if (initial) {
766                 color_dialog.get_colorsel()->set_current_color (*initial);
767         }
768
769         color_dialog.show_all ();
770         color_picked = false;
771         picked = false;
772
773         Main::run();
774
775         color_dialog.hide_all ();
776
777         if (color_picked) {
778                 Gdk::Color f_rgba = color_dialog.get_colorsel()->get_current_color ();
779                 color.set_red(f_rgba.get_red());
780                 color.set_green(f_rgba.get_green());
781                 color.set_blue(f_rgba.get_blue());
782
783                 picked = true;
784         }
785
786         return color;
787 }
788
789 void
790 UI::color_selection_done (bool status)
791 {
792         color_picked = status;
793         Main::quit ();
794 }
795
796 bool
797 UI::color_selection_deleted (GdkEventAny* /*ev*/)
798 {
799         Main::quit ();
800         return true;
801 }