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 (850,100);
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 if (path.length() == 0) {
126 if (access (path.c_str(), R_OK)) {
127 error << "UI: couldn't find rc file \""
134 RC rc (path.c_str());
135 // RC::reset_styles (Gtk::Settings::get_default());
136 gtk_rc_reset_styles (gtk_settings_get_default());
137 theme_changed.emit();
140 return 0; //Don't continue on every time there is a theme change
143 /* have to pack widgets into a toplevel window so that styles will stick */
145 Window temp_window (WINDOW_TOPLEVEL);
151 RefPtr<Gtk::Style> style;
152 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
154 box.pack_start (a_widget1);
155 box.pack_start (a_widget2);
156 box.pack_start (a_widget3);
157 box.pack_start (a_widget4);
159 error_ptag = buffer->create_tag();
160 error_mtag = buffer->create_tag();
161 fatal_ptag = buffer->create_tag();
162 fatal_mtag = buffer->create_tag();
163 warning_ptag = buffer->create_tag();
164 warning_mtag = buffer->create_tag();
165 info_ptag = buffer->create_tag();
166 info_mtag = buffer->create_tag();
168 a_widget1.set_name ("FatalMessage");
169 a_widget1.ensure_style ();
170 style = a_widget1.get_style();
172 fatal_ptag->property_font_desc().set_value(style->get_font());
173 fatal_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE));
174 fatal_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE));
175 fatal_mtag->property_font_desc().set_value(style->get_font());
176 fatal_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL));
177 fatal_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL));
179 a_widget2.set_name ("ErrorMessage");
180 a_widget2.ensure_style ();
181 style = a_widget2.get_style();
183 error_ptag->property_font_desc().set_value(style->get_font());
184 error_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE));
185 error_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE));
186 error_mtag->property_font_desc().set_value(style->get_font());
187 error_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL));
188 error_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL));
190 a_widget3.set_name ("WarningMessage");
191 a_widget3.ensure_style ();
192 style = a_widget3.get_style();
194 warning_ptag->property_font_desc().set_value(style->get_font());
195 warning_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE));
196 warning_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE));
197 warning_mtag->property_font_desc().set_value(style->get_font());
198 warning_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL));
199 warning_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL));
201 a_widget4.set_name ("InfoMessage");
202 a_widget4.ensure_style ();
203 style = a_widget4.get_style();
205 info_ptag->property_font_desc().set_value(style->get_font());
206 info_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE));
207 info_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE));
208 info_mtag->property_font_desc().set_value(style->get_font());
209 info_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL));
210 info_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL));
216 UI::run (Receiver &old_receiver)
223 /* stop the old receiver (text/console) once we hit the first idle */
225 Glib::signal_idle().connect (bind_return (mem_fun (old_receiver, &Receiver::hangup), false));
246 pthread_kill (gui_thread, SIGKILL);
253 UIRequest *req = get_request (Quit);
262 static bool idle_quit ()
271 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
274 Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
279 UI::touch_display (Touchable *display)
281 UIRequest *req = get_request (TouchDisplay);
287 req->display = display;
293 UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
295 UIRequest *req = get_request (SetTip);
309 UI::set_state (Widget *w, StateType state)
311 UIRequest *req = get_request (StateChange);
317 req->new_state = state;
324 UI::idle_add (int (*func)(void *), void *arg)
326 UIRequest *req = get_request (AddIdle);
332 req->function = func;
338 /* END abstract_ui interfaces */
341 UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition /*cond*/)
345 /* flush (nonblocking) pipe */
347 while (read (fd, buf, 256) > 0) {}
349 ((UI *) arg)->handle_ui_requests ();
353 UI::do_request (UIRequest* req)
355 if (req->type == ErrorMessage) {
357 process_error_message (req->chn, req->msg);
358 free (const_cast<char*>(req->msg)); /* it was strdup'ed */
359 req->msg = 0; /* don't free it again in the destructor */
361 } else if (req->type == Quit) {
365 } else if (req->type == CallSlot) {
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()) {
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);
544 title += ": Fatal Error";
545 win.set_title (title);
547 win.set_position (WIN_POS_MOUSE);
548 win.set_border_width (12);
550 win.get_vbox()->pack_start (label, true, true);
551 hpacker.pack_start (quit, true, false);
552 win.get_vbox()->pack_start (hpacker, false, false);
554 quit.signal_clicked().connect(mem_fun(*this,&UI::quit));
557 win.set_modal (true);
565 UI::popup_error (const char *text)
569 if (!caller_is_ui_thread()) {
570 error << "non-UI threads can't use UI::popup_error"
575 pup = new PopUp (WIN_POS_MOUSE, 0, true);
576 pup->set_text (text);
582 int gdk_quartz_in_carbon_menu_event_handler ();
590 /* as of february 11th 2008, gtk/osx has a problem in that mac menu events
591 are handled using Carbon with an "internal" event handling system that
592 doesn't pass things back to the glib/gtk main loop. this makes
593 gtk_main_iteration() block if we call it while in a menu event handler
594 because glib gets confused and thinks there are two threads running
597 this hack (relies on code in gtk2_ardour/sync-menu.c) works
601 if (gdk_quartz_in_carbon_menu_event_handler()) {
605 if (!caller_is_ui_thread()) {
606 error << "non-UI threads cannot call UI::flush_pending()"
611 gtk_main_iteration();
613 while (gtk_events_pending()) {
614 gtk_main_iteration();
619 UI::just_hide_it (GdkEventAny */*ev*/, Window *win)
626 UI::get_color (const string& prompt, bool& picked, const Gdk::Color* initial)
630 ColorSelectionDialog color_dialog (prompt);
632 color_dialog.set_modal (true);
633 color_dialog.get_cancel_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), false));
634 color_dialog.get_ok_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), true));
635 color_dialog.signal_delete_event().connect (mem_fun (*this, &UI::color_selection_deleted));
638 color_dialog.get_colorsel()->set_current_color (*initial);
641 color_dialog.show_all ();
642 color_picked = false;
647 color_dialog.hide_all ();
650 Gdk::Color f_rgba = color_dialog.get_colorsel()->get_current_color ();
651 color.set_red(f_rgba.get_red());
652 color.set_green(f_rgba.get_green());
653 color.set_blue(f_rgba.get_blue());
662 UI::color_selection_done (bool status)
664 color_picked = status;
669 UI::color_selection_deleted (GdkEventAny */*ev*/)