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/application.h>
37 #include <gtkmm2ext/gtk_ui.h>
38 #include <gtkmm2ext/textviewer.h>
39 #include <gtkmm2ext/popup.h>
40 #include <gtkmm2ext/utils.h>
41 #include <gtkmm2ext/window_title.h>
42 #include <gtkmm2ext/actions.h>
46 using namespace Gtkmm2ext;
54 BaseUI::RequestType Gtkmm2ext::NullMessage = BaseUI::new_request_type();
55 BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
56 BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type();
57 BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type();
58 BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type();
59 BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type();
60 BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
62 #include "pbd/abstract_ui.cc" /* instantiate the template */
64 UI::UI (string namestr, int *argc, char ***argv)
65 : AbstractUI<UIRequest> (namestr)
67 theMain = new Main (argc, argv);
68 #ifndef GTK_NEW_TOOLTIP_API
77 fatal << "duplicate UI requested" << endmsg;
81 /* the GUI event loop runs in the main thread of the app,
82 which is assumed to have called this.
85 run_loop_thread = Thread::self();
87 /* store "this" as the UI-for-thread of this thread, same argument
91 set_event_loop_for_thread (this);
93 /* attach our request source to the default main context */
95 request_channel.ios()->attach (MainContext::get_default());
97 errors = new TextViewer (800,600);
98 errors->text().set_editable (false);
99 errors->text().set_name ("ErrorText");
100 errors->signal_unmap().connect (sigc::bind (sigc::ptr_fun (&ActionManager::uncheck_toggleaction), X_("<Actions>/Editor/toggle-log-window")));
102 Glib::set_application_name(namestr);
104 WindowTitle title(Glib::get_application_name());
106 errors->set_title (title.get_string());
108 errors->dismiss_button().set_name ("ErrorLogCloseButton");
109 errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
110 errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
112 //load_rcfile (rcfile);
114 /* instantiate the Application singleton */
116 Application::instance();
125 UI::caller_is_ui_thread ()
127 return Thread::self() == run_loop_thread;
131 UI::load_rcfile (string path, bool themechange)
133 /* Yes, pointers to Glib::RefPtr. If these are not kept around,
134 * a segfault somewhere deep in the wonderfully robust glib will result.
135 * This does not occur if wiget.get_style is used instead of rc.get_style below,
136 * except that doesn't actually work...
139 return 0; //Disable theme change completely till we figure this out...
141 static Glib::RefPtr<Style>* fatal_style = 0;
142 static Glib::RefPtr<Style>* error_style = 0;
143 static Glib::RefPtr<Style>* warning_style = 0;
144 static Glib::RefPtr<Style>* info_style = 0;
146 if (path.length() == 0) {
150 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
151 error << "UI: couldn't find rc file \""
158 RC rc (path.c_str());
159 //this is buggy in gtkmm for some reason, so use C
160 //RC::reset_styles (Gtk::Settings::get_default());
161 gtk_rc_reset_styles (gtk_settings_get_default());
163 theme_changed.emit();
166 return 0; //Don't continue on every time there is a theme change
169 /* have to pack widgets into a toplevel window so that styles will stick */
171 Window temp_window (WINDOW_TOPLEVEL);
172 temp_window.ensure_style ();
177 Label warning_widget;
179 RefPtr<Gtk::Style> style;
180 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
182 box.pack_start (fatal_widget);
183 box.pack_start (error_widget);
184 box.pack_start (warning_widget);
185 box.pack_start (info_widget);
187 error_ptag = buffer->create_tag();
188 error_mtag = buffer->create_tag();
189 fatal_ptag = buffer->create_tag();
190 fatal_mtag = buffer->create_tag();
191 warning_ptag = buffer->create_tag();
192 warning_mtag = buffer->create_tag();
193 info_ptag = buffer->create_tag();
194 info_mtag = buffer->create_tag();
196 fatal_widget.set_name ("FatalMessage");
199 /* This next line and the similar ones below are sketchily
200 * guessed to fix #2885. I think maybe that problems occur
201 * because with gtk_rc_get_style (to quote its docs) "no
202 * refcount is added to the returned style". So I've switched
203 * this to use Glib::wrap with take_copy == true, which requires
204 * all the nasty casts and calls to plain-old-C GTK.
206 * At worst I think this causes a memory leak; at least it appears
209 * I could be wrong about any or all of the above.
211 fatal_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (fatal_widget.gobj())), true));
213 fatal_ptag->property_font_desc().set_value((*fatal_style)->get_font());
214 fatal_ptag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_ACTIVE));
215 fatal_ptag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_ACTIVE));
216 fatal_mtag->property_font_desc().set_value((*fatal_style)->get_font());
217 fatal_mtag->property_foreground_gdk().set_value((*fatal_style)->get_fg(STATE_NORMAL));
218 fatal_mtag->property_background_gdk().set_value((*fatal_style)->get_bg(STATE_NORMAL));
220 error_widget.set_name ("ErrorMessage");
222 error_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (error_widget.gobj())), true));
224 error_ptag->property_font_desc().set_value((*error_style)->get_font());
225 error_ptag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_ACTIVE));
226 error_ptag->property_background_gdk().set_value((*error_style)->get_bg(STATE_ACTIVE));
227 error_mtag->property_font_desc().set_value((*error_style)->get_font());
228 error_mtag->property_foreground_gdk().set_value((*error_style)->get_fg(STATE_NORMAL));
229 error_mtag->property_background_gdk().set_value((*error_style)->get_bg(STATE_NORMAL));
231 warning_widget.set_name ("WarningMessage");
232 delete warning_style;
233 warning_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (warning_widget.gobj())), true));
235 warning_ptag->property_font_desc().set_value((*warning_style)->get_font());
236 warning_ptag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_ACTIVE));
237 warning_ptag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_ACTIVE));
238 warning_mtag->property_font_desc().set_value((*warning_style)->get_font());
239 warning_mtag->property_foreground_gdk().set_value((*warning_style)->get_fg(STATE_NORMAL));
240 warning_mtag->property_background_gdk().set_value((*warning_style)->get_bg(STATE_NORMAL));
242 info_widget.set_name ("InfoMessage");
244 info_style = new Glib::RefPtr<Style> (Glib::wrap (gtk_rc_get_style (reinterpret_cast<GtkWidget*> (info_widget.gobj())), true));
246 info_ptag->property_font_desc().set_value((*info_style)->get_font());
247 info_ptag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_ACTIVE));
248 info_ptag->property_background_gdk().set_value((*info_style)->get_bg(STATE_ACTIVE));
249 info_mtag->property_font_desc().set_value((*info_style)->get_font());
250 info_mtag->property_foreground_gdk().set_value((*info_style)->get_fg(STATE_NORMAL));
251 info_mtag->property_background_gdk().set_value((*info_style)->get_bg(STATE_NORMAL));
257 UI::run (Receiver &old_receiver)
264 /* stop the old receiver (text/console) once we hit the first idle */
266 Glib::signal_idle().connect (bind_return (mem_fun (old_receiver, &Receiver::hangup), false));
286 UIRequest *req = get_request (Quit);
295 static bool idle_quit ()
304 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
307 Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
312 UI::touch_display (Touchable *display)
314 UIRequest *req = get_request (TouchDisplay);
320 req->display = display;
326 UI::set_tip (Widget &w, const gchar *tip)
328 set_tip(&w, tip, "");
332 UI::set_tip (Widget &w, const std::string& tip)
334 set_tip(&w, tip.c_str(), "");
338 UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
340 UIRequest *req = get_request (SetTip);
342 std::string msg(tip);
344 Glib::RefPtr<Gtk::Action> action = w->get_action();
347 ustring ap = action->get_accel_path();
349 bool has_key = ActionManager::lookup_entry(ap, key);
350 if (has_key && key.get_abbrev() != "") {
351 msg.append("\n\n Key: ").append(key.get_abbrev());
361 req->msg = msg.c_str();
368 UI::set_state (Widget *w, StateType state)
370 UIRequest *req = get_request (StateChange);
376 req->new_state = state;
383 UI::idle_add (int (*func)(void *), void *arg)
385 UIRequest *req = get_request (AddIdle);
391 req->function = func;
397 /* END abstract_ui interfaces */
399 /** Create a PBD::EventLoop::InvalidationRecord and attach a callback
400 * to a given sigc::trackable so that PBD::EventLoop::invalidate_request
401 * is called when that trackable is destroyed.
403 PBD::EventLoop::InvalidationRecord*
404 __invalidator (sigc::trackable& trackable, const char* file, int line)
406 PBD::EventLoop::InvalidationRecord* ir = new PBD::EventLoop::InvalidationRecord;
411 trackable.add_destroy_notify_callback (ir, PBD::EventLoop::invalidate_request);
417 UI::do_request (UIRequest* req)
419 if (req->type == ErrorMessage) {
421 process_error_message (req->chn, req->msg);
422 free (const_cast<char*>(req->msg)); /* it was strdup'ed */
423 req->msg = 0; /* don't free it again in the destructor */
425 } else if (req->type == Quit) {
429 } else if (req->type == CallSlot) {
431 if (getenv ("DEBUG_THREADED_SIGNALS")) {
432 cerr << "call slot for " << name() << endl;
437 } else if (req->type == TouchDisplay) {
439 req->display->touch ();
440 if (req->display->delete_after_touch()) {
444 } else if (req->type == StateChange) {
446 req->widget->set_state (req->new_state);
448 } else if (req->type == SetTip) {
450 #ifdef GTK_NEW_TOOLTIP_API
451 /* even if the installed GTK is up to date,
452 at present (November 2008) our included
453 version of gtkmm is not. so use the GTK
454 API that we've verified has the right function.
456 gtk_widget_set_tooltip_text (req->widget->gobj(), req->msg);
458 tips->set_tip (*req->widget, req->msg, "");
463 error << "GtkUI: unknown request type "
469 /*======================================================================
471 ======================================================================*/
474 UI::receive (Transmitter::Channel chn, const char *str)
476 if (caller_is_ui_thread()) {
477 process_error_message (chn, str);
479 UIRequest* req = get_request (ErrorMessage);
486 req->msg = strdup (str);
492 #define OLD_STYLE_ERRORS 1
495 UI::process_error_message (Transmitter::Channel chn, const char *str)
498 RefPtr<TextBuffer::Tag> ptag;
499 RefPtr<TextBuffer::Tag> mtag;
502 bool fatal_received = false;
503 #ifndef OLD_STYLE_ERRORS
504 PopUp* popup = new PopUp (WIN_POS_CENTER, 0, true);
508 case Transmitter::Fatal:
509 prefix = "[FATAL]: ";
513 fatal_received = true;
515 case Transmitter::Error:
517 prefix = "[ERROR]: ";
522 popup->set_name ("ErrorMessage");
523 popup->set_text (str);
528 case Transmitter::Info:
535 popup->set_name ("InfoMessage");
536 popup->set_text (str);
542 case Transmitter::Warning:
544 prefix = "[WARNING]: ";
549 popup->set_name ("WarningMessage");
550 popup->set_text (str);
556 /* no choice but to use text/console output here */
557 cerr << "programmer error in UI::check_error_messages (channel = " << chn << ")\n";
561 errors->text().get_buffer()->begin_user_action();
563 if (fatal_received) {
567 display_message (prefix, prefix_len, ptag, mtag, str);
569 if (!errors->is_visible() && chn != Transmitter::Info) {
574 errors->text().get_buffer()->end_user_action();
580 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
585 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
587 if (tact->get_active()) {
588 errors->set_position (WIN_POS_MOUSE);
596 UI::display_message (const char *prefix, gint /*prefix_len*/, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg)
598 RefPtr<TextBuffer> buffer (errors->text().get_buffer());
600 buffer->insert_with_tag(buffer->end(), prefix, ptag);
601 buffer->insert_with_tag(buffer->end(), msg, mtag);
602 buffer->insert_with_tag(buffer->end(), "\n", mtag);
604 errors->scroll_to_bottom ();
608 UI::handle_fatal (const char *message)
611 Label label (message);
612 Button quit (_("Press To Exit"));
615 win.set_default_size (400, 100);
617 WindowTitle title(Glib::get_application_name());
618 title += ": Fatal Error";
619 win.set_title (title.get_string());
621 win.set_position (WIN_POS_MOUSE);
622 win.set_border_width (12);
624 win.get_vbox()->pack_start (label, true, true);
625 hpacker.pack_start (quit, true, false);
626 win.get_vbox()->pack_start (hpacker, false, false);
628 quit.signal_clicked().connect(mem_fun(*this,&UI::quit));
631 win.set_modal (true);
639 UI::popup_error (const string& text)
643 if (!caller_is_ui_thread()) {
644 error << "non-UI threads can't use UI::popup_error"
649 pup = new PopUp (WIN_POS_MOUSE, 0, true);
650 pup->set_text (text);
658 if (!caller_is_ui_thread()) {
659 error << "non-UI threads cannot call UI::flush_pending()"
664 gtk_main_iteration();
666 while (gtk_events_pending()) {
667 gtk_main_iteration();
672 UI::just_hide_it (GdkEventAny */*ev*/, Window *win)
679 UI::get_color (const string& prompt, bool& picked, const Gdk::Color* initial)
683 ColorSelectionDialog color_dialog (prompt);
685 color_dialog.set_modal (true);
686 color_dialog.get_cancel_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), false));
687 color_dialog.get_ok_button()->signal_clicked().connect (bind (mem_fun (*this, &UI::color_selection_done), true));
688 color_dialog.signal_delete_event().connect (mem_fun (*this, &UI::color_selection_deleted));
691 color_dialog.get_colorsel()->set_current_color (*initial);
694 color_dialog.show_all ();
695 color_picked = false;
700 color_dialog.hide_all ();
703 Gdk::Color f_rgba = color_dialog.get_colorsel()->get_current_color ();
704 color.set_red(f_rgba.get_red());
705 color.set_green(f_rgba.get_green());
706 color.set_blue(f_rgba.get_blue());
715 UI::color_selection_done (bool status)
717 color_picked = status;
722 UI::color_selection_deleted (GdkEventAny */*ev*/)