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 /* attach our request source to the default main context */
86 request_channel.ios()->attach (MainContext::get_default());
88 errors = new TextViewer (800,600);
89 errors->text().set_editable (false);
90 errors->text().set_name ("ErrorText");
92 Glib::set_application_name(namestr);
94 WindowTitle title(Glib::get_application_name());
96 errors->set_title (title.get_string());
98 errors->dismiss_button().set_name ("ErrorLogCloseButton");
99 errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
100 errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
102 //load_rcfile (rcfile);
111 UI::caller_is_ui_thread ()
113 return Thread::self() == run_loop_thread;
117 UI::load_rcfile (string path, bool themechange)
119 /* Yes, pointers to Glib::RefPtr. If these are not kept around,
120 * a segfault somewhere deep in the wonderfully robust glib will result.
121 * This does not occur if wiget.get_style is used instead of rc.get_style below,
122 * except that doesn't actually work...
125 static Glib::RefPtr<Style>* fatal_style = 0;
126 static Glib::RefPtr<Style>* error_style = 0;
127 static Glib::RefPtr<Style>* warning_style = 0;
128 static Glib::RefPtr<Style>* info_style = 0;
130 if (path.length() == 0) {
134 if (access (path.c_str(), R_OK)) {
135 error << "UI: couldn't find rc file \""
142 RC rc (path.c_str());
143 //RC::reset_styles (Gtk::Settings::get_default());
144 gtk_rc_reset_styles (gtk_settings_get_default());
145 theme_changed.emit();
148 return 0; //Don't continue on every time there is a theme change
151 /* have to pack widgets into a toplevel window so that styles will stick */
153 Window temp_window (WINDOW_TOPLEVEL);
154 temp_window.ensure_style ();
159 Label warning_widget;
161 RefPtr<Gtk::Style> style;
162 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
164 box.pack_start (fatal_widget);
165 box.pack_start (error_widget);
166 box.pack_start (warning_widget);
167 box.pack_start (info_widget);
169 error_ptag = buffer->create_tag();
170 error_mtag = buffer->create_tag();
171 fatal_ptag = buffer->create_tag();
172 fatal_mtag = buffer->create_tag();
173 warning_ptag = buffer->create_tag();
174 warning_mtag = buffer->create_tag();
175 info_ptag = buffer->create_tag();
176 info_mtag = buffer->create_tag();
178 fatal_widget.set_name ("FatalMessage");
180 fatal_style = new Glib::RefPtr<Style>(rc.get_style(fatal_widget));
182 fatal_ptag->property_font_desc().set_value((*fatal_style)->get_font());
183 fatal_ptag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_ACTIVE));
184 fatal_ptag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_ACTIVE));
185 fatal_mtag->property_font_desc().set_value((*fatal_style)->get_font());
186 fatal_mtag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_NORMAL));
187 fatal_mtag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_NORMAL));
189 error_widget.set_name ("ErrorMessage");
191 error_style = new Glib::RefPtr<Style>(rc.get_style(error_widget));
193 error_ptag->property_font_desc().set_value((*error_style)->get_font());
194 error_ptag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_ACTIVE));
195 error_ptag->property_background_gdk().set_value((*error_style)->get_bg(STATE_ACTIVE));
196 error_mtag->property_font_desc().set_value((*error_style)->get_font());
197 error_mtag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_NORMAL));
198 error_mtag->property_background_gdk().set_value((*error_style)->get_bg(STATE_NORMAL));
200 warning_widget.set_name ("WarningMessage");
201 delete warning_style;
202 warning_style = new Glib::RefPtr<Style>(rc.get_style(warning_widget));
204 warning_ptag->property_font_desc().set_value((*warning_style)->get_font());
205 warning_ptag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_ACTIVE));
206 warning_ptag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_ACTIVE));
207 warning_mtag->property_font_desc().set_value((*warning_style)->get_font());
208 warning_mtag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_NORMAL));
209 warning_mtag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_NORMAL));
211 info_widget.set_name ("InfoMessage");
213 info_style = new Glib::RefPtr<Style>(rc.get_style(info_widget));
215 info_ptag->property_font_desc().set_value((*info_style)->get_font());
216 info_ptag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_ACTIVE));
217 info_ptag->property_background_gdk().set_value((*info_style)->get_bg(STATE_ACTIVE));
218 info_mtag->property_font_desc().set_value((*info_style)->get_font());
219 info_mtag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_NORMAL));
220 info_mtag->property_background_gdk().set_value((*info_style)->get_bg(STATE_NORMAL));
226 UI::run (Receiver &old_receiver)
233 /* stop the old receiver (text/console) once we hit the first idle */
235 Glib::signal_idle().connect (bind_return (mem_fun (old_receiver, &Receiver::hangup), false));
255 UIRequest *req = get_request (Quit);
264 static bool idle_quit ()
273 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
276 Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
281 UI::touch_display (Touchable *display)
283 UIRequest *req = get_request (TouchDisplay);
289 req->display = display;
295 UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
297 UIRequest *req = get_request (SetTip);
311 UI::set_state (Widget *w, StateType state)
313 UIRequest *req = get_request (StateChange);
319 req->new_state = state;
326 UI::idle_add (int (*func)(void *), void *arg)
328 UIRequest *req = get_request (AddIdle);
334 req->function = func;
340 /* END abstract_ui interfaces */
343 UI::do_request (UIRequest* req)
345 if (req->type == ErrorMessage) {
347 process_error_message (req->chn, req->msg);
348 free (const_cast<char*>(req->msg)); /* it was strdup'ed */
349 req->msg = 0; /* don't free it again in the destructor */
351 } else if (req->type == Quit) {
355 } else if (req->type == CallSlot) {
359 } else if (req->type == TouchDisplay) {
361 req->display->touch ();
362 if (req->display->delete_after_touch()) {
366 } else if (req->type == StateChange) {
368 req->widget->set_state (req->new_state);
370 } else if (req->type == SetTip) {
372 #ifdef GTK_NEW_TOOLTIP_API
373 /* even if the installed GTK is up to date,
374 at present (November 2008) our included
375 version of gtkmm is not. so use the GTK
376 API that we've verified has the right function.
378 gtk_widget_set_tooltip_text (req->widget->gobj(), req->msg);
380 tips->set_tip (*req->widget, req->msg, "");
385 error << "GtkUI: unknown request type "
391 /*======================================================================
393 ======================================================================*/
396 UI::receive (Transmitter::Channel chn, const char *str)
398 if (caller_is_ui_thread()) {
399 process_error_message (chn, str);
401 UIRequest* req = get_request (ErrorMessage);
408 req->msg = strdup (str);
414 #define OLD_STYLE_ERRORS 1
417 UI::process_error_message (Transmitter::Channel chn, const char *str)
420 RefPtr<TextBuffer::Tag> ptag;
421 RefPtr<TextBuffer::Tag> mtag;
424 bool fatal_received = false;
425 #ifndef OLD_STYLE_ERRORS
426 PopUp* popup = new PopUp (WIN_POS_CENTER, 0, true);
430 case Transmitter::Fatal:
431 prefix = "[FATAL]: ";
435 fatal_received = true;
437 case Transmitter::Error:
439 prefix = "[ERROR]: ";
444 popup->set_name ("ErrorMessage");
445 popup->set_text (str);
450 case Transmitter::Info:
457 popup->set_name ("InfoMessage");
458 popup->set_text (str);
464 case Transmitter::Warning:
466 prefix = "[WARNING]: ";
471 popup->set_name ("WarningMessage");
472 popup->set_text (str);
478 /* no choice but to use text/console output here */
479 cerr << "programmer error in UI::check_error_messages (channel = " << chn << ")\n";
483 errors->text().get_buffer()->begin_user_action();
485 if (fatal_received) {
489 display_message (prefix, prefix_len, ptag, mtag, str);
491 if (!errors->is_visible() && chn != Transmitter::Info) {
496 errors->text().get_buffer()->end_user_action();
502 if (!errors->is_visible()) {
503 errors->set_position (WIN_POS_MOUSE);
511 UI::display_message (const char *prefix, gint /*prefix_len*/, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg)
513 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
515 buffer->insert_with_tag(buffer->end(), prefix, ptag);
516 buffer->insert_with_tag(buffer->end(), msg, mtag);
517 buffer->insert_with_tag(buffer->end(), "\n", mtag);
519 errors->scroll_to_bottom ();
523 UI::handle_fatal (const char *message)
526 Label label (message);
527 Button quit (_("Press To Exit"));
530 win.set_default_size (400, 100);
532 WindowTitle title(Glib::get_application_name());
533 title += ": Fatal Error";
534 win.set_title (title.get_string());
536 win.set_position (WIN_POS_MOUSE);
537 win.set_border_width (12);
539 win.get_vbox()->pack_start (label, true, true);
540 hpacker.pack_start (quit, true, false);
541 win.get_vbox()->pack_start (hpacker, false, false);
543 quit.signal_clicked().connect(mem_fun(*this,&UI::quit));
546 win.set_modal (true);
554 UI::popup_error (const char *text)
558 if (!caller_is_ui_thread()) {
559 error << "non-UI threads can't use UI::popup_error"
564 pup = new PopUp (WIN_POS_MOUSE, 0, true);
565 pup->set_text (text);
571 int gdk_quartz_in_carbon_menu_event_handler ();
579 /* as of february 11th 2008, gtk/osx has a problem in that mac menu events
580 are handled using Carbon with an "internal" event handling system that
581 doesn't pass things back to the glib/gtk main loop. this makes
582 gtk_main_iteration() block if we call it while in a menu event handler
583 because glib gets confused and thinks there are two threads running
586 this hack (relies on code in gtk2_ardour/sync-menu.c) works
590 if (gdk_quartz_in_carbon_menu_event_handler()) {
594 if (!caller_is_ui_thread()) {
595 error << "non-UI threads cannot call UI::flush_pending()"
600 gtk_main_iteration();
602 while (gtk_events_pending()) {
603 gtk_main_iteration();
608 UI::just_hide_it (GdkEventAny */*ev*/, Window *win)
615 UI::get_color (const string& prompt, bool& picked, const Gdk::Color* initial)
619 ColorSelectionDialog color_dialog (prompt);
621 color_dialog.set_modal (true);
622 color_dialog.get_cancel_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), false));
623 color_dialog.get_ok_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), true));
624 color_dialog.signal_delete_event().connect (mem_fun (*this, &UI::color_selection_deleted));
627 color_dialog.get_colorsel()->set_current_color (*initial);
630 color_dialog.show_all ();
631 color_picked = false;
636 color_dialog.hide_all ();
639 Gdk::Color f_rgba = color_dialog.get_colorsel()->get_current_color ();
640 color.set_red(f_rgba.get_red());
641 color.set_green(f_rgba.get_green());
642 color.set_blue(f_rgba.get_blue());
651 UI::color_selection_done (bool status)
653 color_picked = status;
658 UI::color_selection_deleted (GdkEventAny */*ev*/)