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;
50 pthread_t UI::gui_thread;
53 BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
54 BaseUI::RequestType Gtkmm2ext::Quit = BaseUI::new_request_type();
55 BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type();
56 BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type();
57 BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type();
58 BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type();
59 BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
61 #include <pbd/abstract_ui.cc> /* instantiate the template */
64 UI::UI (string namestr, int *argc, char ***argv)
65 : AbstractUI<UIRequest> (namestr, true)
67 theMain = new Main (argc, argv);
68 #ifndef GTK_NEW_TOOLTIP_API
76 gui_thread = pthread_self ();
78 fatal << "duplicate UI requested" << endmsg;
82 /* add the pipe to the select/poll loop that GDK does */
84 gdk_input_add (signal_pipe[0],
86 UI::signal_pipe_callback,
89 errors = new TextViewer (800,600);
90 errors->text().set_editable (false);
91 errors->text().set_name ("ErrorText");
93 Glib::set_application_name(namestr);
95 WindowTitle title(Glib::get_application_name());
97 errors->set_title (title.get_string());
99 errors->dismiss_button().set_name ("ErrorLogCloseButton");
100 errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
101 errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
103 register_thread (pthread_self(), X_("GUI"));
105 //load_rcfile (rcfile);
114 UI::caller_is_ui_thread ()
116 return pthread_equal (gui_thread, pthread_self());
120 UI::load_rcfile (string path, bool themechange)
122 /* Yes, pointers to Glib::RefPtr. If these are not kept around,
123 * a segfault somewhere deep in the wonderfully robust glib will result.
124 * This does not occur if wiget.get_style is used instead of rc.get_style below,
125 * except that doesn't actually work... */
126 static Glib::RefPtr<Style>* fatal_style = 0;
127 static Glib::RefPtr<Style>* error_style = 0;
128 static Glib::RefPtr<Style>* warning_style = 0;
129 static Glib::RefPtr<Style>* info_style = 0;
131 if (path.length() == 0) {
135 if (access (path.c_str(), R_OK)) {
136 error << "UI: couldn't find rc file \""
143 RC rc (path.c_str());
144 //RC::reset_styles (Gtk::Settings::get_default());
145 gtk_rc_reset_styles (gtk_settings_get_default());
146 theme_changed.emit();
149 return 0; //Don't continue on every time there is a theme change
152 /* have to pack widgets into a toplevel window so that styles will stick */
154 Window temp_window (WINDOW_TOPLEVEL);
155 temp_window.ensure_style ();
160 Label warning_widget;
162 RefPtr<Gtk::Style> style;
163 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
165 box.pack_start (fatal_widget);
166 box.pack_start (error_widget);
167 box.pack_start (warning_widget);
168 box.pack_start (info_widget);
170 error_ptag = buffer->create_tag();
171 error_mtag = buffer->create_tag();
172 fatal_ptag = buffer->create_tag();
173 fatal_mtag = buffer->create_tag();
174 warning_ptag = buffer->create_tag();
175 warning_mtag = buffer->create_tag();
176 info_ptag = buffer->create_tag();
177 info_mtag = buffer->create_tag();
179 fatal_widget.set_name ("FatalMessage");
181 fatal_style = new Glib::RefPtr<Style>(rc.get_style(fatal_widget));
183 fatal_ptag->property_font_desc().set_value((*fatal_style)->get_font());
184 fatal_ptag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_ACTIVE));
185 fatal_ptag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_ACTIVE));
186 fatal_mtag->property_font_desc().set_value((*fatal_style)->get_font());
187 fatal_mtag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_NORMAL));
188 fatal_mtag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_NORMAL));
190 error_widget.set_name ("ErrorMessage");
192 error_style = new Glib::RefPtr<Style>(rc.get_style(error_widget));
194 error_ptag->property_font_desc().set_value((*error_style)->get_font());
195 error_ptag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_ACTIVE));
196 error_ptag->property_background_gdk().set_value((*error_style)->get_bg(STATE_ACTIVE));
197 error_mtag->property_font_desc().set_value((*error_style)->get_font());
198 error_mtag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_NORMAL));
199 error_mtag->property_background_gdk().set_value((*error_style)->get_bg(STATE_NORMAL));
201 warning_widget.set_name ("WarningMessage");
202 delete warning_style;
203 warning_style = new Glib::RefPtr<Style>(rc.get_style(warning_widget));
205 warning_ptag->property_font_desc().set_value((*warning_style)->get_font());
206 warning_ptag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_ACTIVE));
207 warning_ptag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_ACTIVE));
208 warning_mtag->property_font_desc().set_value((*warning_style)->get_font());
209 warning_mtag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_NORMAL));
210 warning_mtag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_NORMAL));
212 info_widget.set_name ("InfoMessage");
214 info_style = new Glib::RefPtr<Style>(rc.get_style(info_widget));
216 info_ptag->property_font_desc().set_value((*info_style)->get_font());
217 info_ptag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_ACTIVE));
218 info_ptag->property_background_gdk().set_value((*info_style)->get_bg(STATE_ACTIVE));
219 info_mtag->property_font_desc().set_value((*info_style)->get_font());
220 info_mtag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_NORMAL));
221 info_mtag->property_background_gdk().set_value((*info_style)->get_bg(STATE_NORMAL));
227 UI::run (Receiver &old_receiver)
234 /* stop the old receiver (text/console) once we hit the first idle */
236 Glib::signal_idle().connect (bind_return (mem_fun (old_receiver, &Receiver::hangup), false));
257 pthread_kill (gui_thread, SIGKILL);
264 UIRequest *req = get_request (Quit);
273 static bool idle_quit ()
282 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
285 Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
290 UI::touch_display (Touchable *display)
292 UIRequest *req = get_request (TouchDisplay);
298 req->display = display;
304 UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
306 UIRequest *req = get_request (SetTip);
320 UI::set_state (Widget *w, StateType state)
322 UIRequest *req = get_request (StateChange);
328 req->new_state = state;
335 UI::idle_add (int (*func)(void *), void *arg)
337 UIRequest *req = get_request (AddIdle);
343 req->function = func;
349 /* END abstract_ui interfaces */
352 UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition /*cond*/)
356 /* flush (nonblocking) pipe */
358 while (read (fd, buf, 256) > 0) {}
360 ((UI *) arg)->handle_ui_requests ();
364 UI::do_request (UIRequest* req)
366 if (req->type == ErrorMessage) {
368 process_error_message (req->chn, req->msg);
369 free (const_cast<char*>(req->msg)); /* it was strdup'ed */
370 req->msg = 0; /* don't free it again in the destructor */
372 } else if (req->type == Quit) {
376 } else if (req->type == CallSlot) {
380 } else if (req->type == TouchDisplay) {
382 req->display->touch ();
383 if (req->display->delete_after_touch()) {
387 } else if (req->type == StateChange) {
389 req->widget->set_state (req->new_state);
391 } else if (req->type == SetTip) {
393 #ifdef GTK_NEW_TOOLTIP_API
394 /* even if the installed GTK is up to date,
395 at present (November 2008) our included
396 version of gtkmm is not. so use the GTK
397 API that we've verified has the right function.
399 gtk_widget_set_tooltip_text (req->widget->gobj(), req->msg);
401 tips->set_tip (*req->widget, req->msg, "");
406 error << "GtkUI: unknown request type "
412 /*======================================================================
414 ======================================================================*/
417 UI::receive (Transmitter::Channel chn, const char *str)
419 if (caller_is_ui_thread()) {
420 process_error_message (chn, str);
422 UIRequest* req = get_request (ErrorMessage);
429 req->msg = strdup (str);
435 #define OLD_STYLE_ERRORS 1
438 UI::process_error_message (Transmitter::Channel chn, const char *str)
441 RefPtr<TextBuffer::Tag> ptag;
442 RefPtr<TextBuffer::Tag> mtag;
445 bool fatal_received = false;
446 #ifndef OLD_STYLE_ERRORS
447 PopUp* popup = new PopUp (WIN_POS_CENTER, 0, true);
451 case Transmitter::Fatal:
452 prefix = "[FATAL]: ";
456 fatal_received = true;
458 case Transmitter::Error:
460 prefix = "[ERROR]: ";
465 popup->set_name ("ErrorMessage");
466 popup->set_text (str);
471 case Transmitter::Info:
478 popup->set_name ("InfoMessage");
479 popup->set_text (str);
485 case Transmitter::Warning:
487 prefix = "[WARNING]: ";
492 popup->set_name ("WarningMessage");
493 popup->set_text (str);
499 /* no choice but to use text/console output here */
500 cerr << "programmer error in UI::check_error_messages (channel = " << chn << ")\n";
504 errors->text().get_buffer()->begin_user_action();
506 if (fatal_received) {
510 display_message (prefix, prefix_len, ptag, mtag, str);
512 if (!errors->is_visible() && chn != Transmitter::Info) {
517 errors->text().get_buffer()->end_user_action();
523 if (!errors->is_visible()) {
524 errors->set_position (WIN_POS_MOUSE);
532 UI::display_message (const char *prefix, gint /*prefix_len*/, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg)
534 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
536 buffer->insert_with_tag(buffer->end(), prefix, ptag);
537 buffer->insert_with_tag(buffer->end(), msg, mtag);
538 buffer->insert_with_tag(buffer->end(), "\n", mtag);
540 errors->scroll_to_bottom ();
544 UI::handle_fatal (const char *message)
547 Label label (message);
548 Button quit (_("Press To Exit"));
551 win.set_default_size (400, 100);
555 title += ": Fatal Error";
556 win.set_title (title);
558 win.set_position (WIN_POS_MOUSE);
559 win.set_border_width (12);
561 win.get_vbox()->pack_start (label, true, true);
562 hpacker.pack_start (quit, true, false);
563 win.get_vbox()->pack_start (hpacker, false, false);
565 quit.signal_clicked().connect(mem_fun(*this,&UI::quit));
568 win.set_modal (true);
576 UI::popup_error (const char *text)
580 if (!caller_is_ui_thread()) {
581 error << "non-UI threads can't use UI::popup_error"
586 pup = new PopUp (WIN_POS_MOUSE, 0, true);
587 pup->set_text (text);
593 int gdk_quartz_in_carbon_menu_event_handler ();
601 /* as of february 11th 2008, gtk/osx has a problem in that mac menu events
602 are handled using Carbon with an "internal" event handling system that
603 doesn't pass things back to the glib/gtk main loop. this makes
604 gtk_main_iteration() block if we call it while in a menu event handler
605 because glib gets confused and thinks there are two threads running
608 this hack (relies on code in gtk2_ardour/sync-menu.c) works
612 if (gdk_quartz_in_carbon_menu_event_handler()) {
616 if (!caller_is_ui_thread()) {
617 error << "non-UI threads cannot call UI::flush_pending()"
622 gtk_main_iteration();
624 while (gtk_events_pending()) {
625 gtk_main_iteration();
630 UI::just_hide_it (GdkEventAny */*ev*/, Window *win)
637 UI::get_color (const string& prompt, bool& picked, const Gdk::Color* initial)
641 ColorSelectionDialog color_dialog (prompt);
643 color_dialog.set_modal (true);
644 color_dialog.get_cancel_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), false));
645 color_dialog.get_ok_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), true));
646 color_dialog.signal_delete_event().connect (mem_fun (*this, &UI::color_selection_deleted));
649 color_dialog.get_colorsel()->set_current_color (*initial);
652 color_dialog.show_all ();
653 color_picked = false;
658 color_dialog.hide_all ();
661 Gdk::Color f_rgba = color_dialog.get_colorsel()->get_current_color ();
662 color.set_red(f_rgba.get_red());
663 color.set_green(f_rgba.get_green());
664 color.set_blue(f_rgba.get_blue());
673 UI::color_selection_done (bool status)
675 color_picked = status;
680 UI::color_selection_deleted (GdkEventAny */*ev*/)