2 Copyright (C) 1999-2005 Paul Barton-Davis
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.
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.
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.
30 #include <pbd/error.h>
31 #include <pbd/touchable.h>
32 #include <pbd/failed_constructor.h>
33 #include <pbd/pthread_utils.h>
34 #include <pbd/stacktrace.h>
36 #include <gtkmm2ext/gtk_ui.h>
37 #include <gtkmm2ext/textviewer.h>
38 #include <gtkmm2ext/popup.h>
39 #include <gtkmm2ext/utils.h>
40 #include <gtkmm2ext/window_title.h>
44 using namespace Gtkmm2ext;
52 BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
53 BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type();
54 BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type();
55 BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type();
56 BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type();
57 BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
59 #include "pbd/abstract_ui.cc" /* instantiate the template */
61 UI::UI (string namestr, int *argc, char ***argv)
62 : AbstractUI<UIRequest> (namestr)
64 theMain = new Main (argc, argv);
65 #ifndef GTK_NEW_TOOLTIP_API
74 fatal << "duplicate UI requested" << endmsg;
78 /* the GUI event loop runs in the main thread of the app,
79 which is assumed to have called this.
82 run_loop_thread = Thread::self();
84 /* store "this" as the UI-for-thread of this thread, same argument
88 set_event_loop_for_thread (this);
90 /* attach our request source to the default main context */
92 request_channel.ios()->attach (MainContext::get_default());
94 errors = new TextViewer (800,600);
95 errors->text().set_editable (false);
96 errors->text().set_name ("ErrorText");
98 Glib::set_application_name(namestr);
100 WindowTitle title(Glib::get_application_name());
102 errors->set_title (title.get_string());
104 errors->dismiss_button().set_name ("ErrorLogCloseButton");
105 errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
106 errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
108 //load_rcfile (rcfile);
117 UI::caller_is_ui_thread ()
119 return Thread::self() == run_loop_thread;
123 UI::load_rcfile (string path, bool themechange)
125 /* Yes, pointers to Glib::RefPtr. If these are not kept around,
126 * a segfault somewhere deep in the wonderfully robust glib will result.
127 * This does not occur if wiget.get_style is used instead of rc.get_style below,
128 * except that doesn't actually work...
131 static Glib::RefPtr<Style>* fatal_style = 0;
132 static Glib::RefPtr<Style>* error_style = 0;
133 static Glib::RefPtr<Style>* warning_style = 0;
134 static Glib::RefPtr<Style>* info_style = 0;
136 if (path.length() == 0) {
140 if (access (path.c_str(), R_OK)) {
141 error << "UI: couldn't find rc file \""
148 RC rc (path.c_str());
149 //RC::reset_styles (Gtk::Settings::get_default());
150 gtk_rc_reset_styles (gtk_settings_get_default());
151 theme_changed.emit();
154 return 0; //Don't continue on every time there is a theme change
157 /* have to pack widgets into a toplevel window so that styles will stick */
159 Window temp_window (WINDOW_TOPLEVEL);
160 temp_window.ensure_style ();
165 Label warning_widget;
167 RefPtr<Gtk::Style> style;
168 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
170 box.pack_start (fatal_widget);
171 box.pack_start (error_widget);
172 box.pack_start (warning_widget);
173 box.pack_start (info_widget);
175 error_ptag = buffer->create_tag();
176 error_mtag = buffer->create_tag();
177 fatal_ptag = buffer->create_tag();
178 fatal_mtag = buffer->create_tag();
179 warning_ptag = buffer->create_tag();
180 warning_mtag = buffer->create_tag();
181 info_ptag = buffer->create_tag();
182 info_mtag = buffer->create_tag();
184 fatal_widget.set_name ("FatalMessage");
186 fatal_style = new Glib::RefPtr<Style>(rc.get_style(fatal_widget));
188 fatal_ptag->property_font_desc().set_value((*fatal_style)->get_font());
189 fatal_ptag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_ACTIVE));
190 fatal_ptag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_ACTIVE));
191 fatal_mtag->property_font_desc().set_value((*fatal_style)->get_font());
192 fatal_mtag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_NORMAL));
193 fatal_mtag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_NORMAL));
195 error_widget.set_name ("ErrorMessage");
197 error_style = new Glib::RefPtr<Style>(rc.get_style(error_widget));
199 error_ptag->property_font_desc().set_value((*error_style)->get_font());
200 error_ptag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_ACTIVE));
201 error_ptag->property_background_gdk().set_value((*error_style)->get_bg(STATE_ACTIVE));
202 error_mtag->property_font_desc().set_value((*error_style)->get_font());
203 error_mtag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_NORMAL));
204 error_mtag->property_background_gdk().set_value((*error_style)->get_bg(STATE_NORMAL));
206 warning_widget.set_name ("WarningMessage");
207 delete warning_style;
208 warning_style = new Glib::RefPtr<Style>(rc.get_style(warning_widget));
210 warning_ptag->property_font_desc().set_value((*warning_style)->get_font());
211 warning_ptag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_ACTIVE));
212 warning_ptag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_ACTIVE));
213 warning_mtag->property_font_desc().set_value((*warning_style)->get_font());
214 warning_mtag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_NORMAL));
215 warning_mtag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_NORMAL));
217 info_widget.set_name ("InfoMessage");
219 info_style = new Glib::RefPtr<Style>(rc.get_style(info_widget));
221 info_ptag->property_font_desc().set_value((*info_style)->get_font());
222 info_ptag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_ACTIVE));
223 info_ptag->property_background_gdk().set_value((*info_style)->get_bg(STATE_ACTIVE));
224 info_mtag->property_font_desc().set_value((*info_style)->get_font());
225 info_mtag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_NORMAL));
226 info_mtag->property_background_gdk().set_value((*info_style)->get_bg(STATE_NORMAL));
232 UI::run (Receiver &old_receiver)
239 /* stop the old receiver (text/console) once we hit the first idle */
241 Glib::signal_idle().connect (bind_return (mem_fun (old_receiver, &Receiver::hangup), false));
261 UIRequest *req = get_request (Quit);
270 static bool idle_quit ()
279 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
282 Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
287 UI::touch_display (Touchable *display)
289 UIRequest *req = get_request (TouchDisplay);
295 req->display = display;
301 UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
303 UIRequest *req = get_request (SetTip);
317 UI::set_state (Widget *w, StateType state)
319 UIRequest *req = get_request (StateChange);
325 req->new_state = state;
332 UI::idle_add (int (*func)(void *), void *arg)
334 UIRequest *req = get_request (AddIdle);
340 req->function = func;
346 /* END abstract_ui interfaces */
349 UI::do_request (UIRequest* req)
351 if (req->type == ErrorMessage) {
353 process_error_message (req->chn, req->msg);
354 free (const_cast<char*>(req->msg)); /* it was strdup'ed */
355 req->msg = 0; /* don't free it again in the destructor */
357 } else if (req->type == Quit) {
361 } else if (req->type == CallSlot) {
363 if (getenv ("DEBUG_THREADED_SIGNALS")) {
364 cerr << "call slot for " << name() << endl;
369 } else if (req->type == TouchDisplay) {
371 req->display->touch ();
372 if (req->display->delete_after_touch()) {
376 } else if (req->type == StateChange) {
378 req->widget->set_state (req->new_state);
380 } else if (req->type == SetTip) {
382 #ifdef GTK_NEW_TOOLTIP_API
383 /* even if the installed GTK is up to date,
384 at present (November 2008) our included
385 version of gtkmm is not. so use the GTK
386 API that we've verified has the right function.
388 gtk_widget_set_tooltip_text (req->widget->gobj(), req->msg);
390 tips->set_tip (*req->widget, req->msg, "");
395 error << "GtkUI: unknown request type "
401 /*======================================================================
403 ======================================================================*/
406 UI::receive (Transmitter::Channel chn, const char *str)
408 if (caller_is_ui_thread()) {
409 process_error_message (chn, str);
411 UIRequest* req = get_request (ErrorMessage);
418 req->msg = strdup (str);
424 #define OLD_STYLE_ERRORS 1
427 UI::process_error_message (Transmitter::Channel chn, const char *str)
430 RefPtr<TextBuffer::Tag> ptag;
431 RefPtr<TextBuffer::Tag> mtag;
434 bool fatal_received = false;
435 #ifndef OLD_STYLE_ERRORS
436 PopUp* popup = new PopUp (WIN_POS_CENTER, 0, true);
440 case Transmitter::Fatal:
441 prefix = "[FATAL]: ";
445 fatal_received = true;
447 case Transmitter::Error:
449 prefix = "[ERROR]: ";
454 popup->set_name ("ErrorMessage");
455 popup->set_text (str);
460 case Transmitter::Info:
467 popup->set_name ("InfoMessage");
468 popup->set_text (str);
474 case Transmitter::Warning:
476 prefix = "[WARNING]: ";
481 popup->set_name ("WarningMessage");
482 popup->set_text (str);
488 /* no choice but to use text/console output here */
489 cerr << "programmer error in UI::check_error_messages (channel = " << chn << ")\n";
493 errors->text().get_buffer()->begin_user_action();
495 if (fatal_received) {
499 display_message (prefix, prefix_len, ptag, mtag, str);
501 if (!errors->is_visible() && chn != Transmitter::Info) {
506 errors->text().get_buffer()->end_user_action();
512 if (!errors->is_visible()) {
513 errors->set_position (WIN_POS_MOUSE);
521 UI::display_message (const char *prefix, gint /*prefix_len*/, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg)
523 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
525 buffer->insert_with_tag(buffer->end(), prefix, ptag);
526 buffer->insert_with_tag(buffer->end(), msg, mtag);
527 buffer->insert_with_tag(buffer->end(), "\n", mtag);
529 errors->scroll_to_bottom ();
533 UI::handle_fatal (const char *message)
536 Label label (message);
537 Button quit (_("Press To Exit"));
540 win.set_default_size (400, 100);
542 WindowTitle title(Glib::get_application_name());
543 title += ": Fatal Error";
544 win.set_title (title.get_string());
546 win.set_position (WIN_POS_MOUSE);
547 win.set_border_width (12);
549 win.get_vbox()->pack_start (label, true, true);
550 hpacker.pack_start (quit, true, false);
551 win.get_vbox()->pack_start (hpacker, false, false);
553 quit.signal_clicked().connect(mem_fun(*this,&UI::quit));
556 win.set_modal (true);
564 UI::popup_error (const char *text)
568 if (!caller_is_ui_thread()) {
569 error << "non-UI threads can't use UI::popup_error"
574 pup = new PopUp (WIN_POS_MOUSE, 0, true);
575 pup->set_text (text);
581 int gdk_quartz_in_carbon_menu_event_handler ();
589 /* as of february 11th 2008, gtk/osx has a problem in that mac menu events
590 are handled using Carbon with an "internal" event handling system that
591 doesn't pass things back to the glib/gtk main loop. this makes
592 gtk_main_iteration() block if we call it while in a menu event handler
593 because glib gets confused and thinks there are two threads running
596 this hack (relies on code in gtk2_ardour/sync-menu.c) works
600 if (gdk_quartz_in_carbon_menu_event_handler()) {
604 if (!caller_is_ui_thread()) {
605 error << "non-UI threads cannot call UI::flush_pending()"
610 gtk_main_iteration();
612 while (gtk_events_pending()) {
613 gtk_main_iteration();
618 UI::just_hide_it (GdkEventAny */*ev*/, Window *win)
625 UI::get_color (const string& prompt, bool& picked, const Gdk::Color* initial)
629 ColorSelectionDialog color_dialog (prompt);
631 color_dialog.set_modal (true);
632 color_dialog.get_cancel_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), false));
633 color_dialog.get_ok_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), true));
634 color_dialog.signal_delete_event().connect (mem_fun (*this, &UI::color_selection_deleted));
637 color_dialog.get_colorsel()->set_current_color (*initial);
640 color_dialog.show_all ();
641 color_picked = false;
646 color_dialog.hide_all ();
649 Gdk::Color f_rgba = color_dialog.get_colorsel()->get_current_color ();
650 color.set_red(f_rgba.get_red());
651 color.set_green(f_rgba.get_green());
652 color.set_blue(f_rgba.get_blue());
661 UI::color_selection_done (bool status)
663 color_picked = status;
668 UI::color_selection_deleted (GdkEventAny */*ev*/)