/* Copyright (C) 1999-2005 Paul Barton-Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "i18n.h" using namespace Gtkmm2ext; using namespace Gtk; using namespace Glib; using namespace PBD; using std::map; pthread_t UI::gui_thread; UI *UI::theGtkUI = 0; BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type(); BaseUI::RequestType Gtkmm2ext::Quit = BaseUI::new_request_type(); BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type(); BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type(); BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type(); BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type(); BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type(); #include /* instantiate the template */ UI::UI (string namestr, int *argc, char ***argv, string rcfile) : AbstractUI (namestr, true) { theMain = new Main (argc, argv); tips = new Tooltips; _active = false; if (!theGtkUI) { theGtkUI = this; gui_thread = pthread_self (); } else { fatal << "duplicate UI requested" << endmsg; /* NOTREACHED */ } /* add the pipe to the select/poll loop that GDK does */ gdk_input_add (signal_pipe[0], GDK_INPUT_READ, UI::signal_pipe_callback, this); errors = new TextViewer (850,100); errors->text().set_editable (false); errors->text().set_name ("ErrorText"); Glib::set_application_name(namestr); WindowTitle title(Glib::get_application_name()); title += _("Log"); errors->set_title (title.get_string()); errors->dismiss_button().set_name ("ErrorLogCloseButton"); errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors)); register_thread (pthread_self(), X_("GUI")); load_rcfile (rcfile); } UI::~UI () { } bool UI::caller_is_ui_thread () { return pthread_equal (gui_thread, pthread_self()); } int UI::load_rcfile (string path) { if (path.length() == 0) { return -1; } if (access (path.c_str(), R_OK)) { error << "UI: couldn't find rc file \"" << path << '"' << endmsg; return -1; } RC rc (path.c_str()); /* have to pack widgets into a toplevel window so that styles will stick */ Window temp_window (WINDOW_TOPLEVEL); HBox box; Label a_widget1; Label a_widget2; Label a_widget3; Label a_widget4; RefPtr style; RefPtr buffer (errors->text().get_buffer()); box.pack_start (a_widget1); box.pack_start (a_widget2); box.pack_start (a_widget3); box.pack_start (a_widget4); error_ptag = buffer->create_tag(); error_mtag = buffer->create_tag(); fatal_ptag = buffer->create_tag(); fatal_mtag = buffer->create_tag(); warning_ptag = buffer->create_tag(); warning_mtag = buffer->create_tag(); info_ptag = buffer->create_tag(); info_mtag = buffer->create_tag(); a_widget1.set_name ("FatalMessage"); a_widget1.ensure_style (); style = a_widget1.get_style(); fatal_ptag->property_font_desc().set_value(style->get_font()); fatal_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE)); fatal_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE)); fatal_mtag->property_font_desc().set_value(style->get_font()); fatal_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL)); fatal_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL)); a_widget2.set_name ("ErrorMessage"); a_widget2.ensure_style (); style = a_widget2.get_style(); error_ptag->property_font_desc().set_value(style->get_font()); error_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE)); error_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE)); error_mtag->property_font_desc().set_value(style->get_font()); error_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL)); error_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL)); a_widget3.set_name ("WarningMessage"); a_widget3.ensure_style (); style = a_widget3.get_style(); warning_ptag->property_font_desc().set_value(style->get_font()); warning_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE)); warning_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE)); warning_mtag->property_font_desc().set_value(style->get_font()); warning_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL)); warning_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL)); a_widget4.set_name ("InfoMessage"); a_widget4.ensure_style (); style = a_widget4.get_style(); info_ptag->property_font_desc().set_value(style->get_font()); info_ptag->property_foreground_gdk().set_value(style->get_fg(STATE_ACTIVE)); info_ptag->property_background_gdk().set_value(style->get_bg(STATE_ACTIVE)); info_mtag->property_font_desc().set_value(style->get_font()); info_mtag->property_foreground_gdk().set_value(style->get_fg(STATE_NORMAL)); info_mtag->property_background_gdk().set_value(style->get_bg(STATE_NORMAL)); return 0; } void UI::run (Receiver &old_receiver) { listen_to (error); listen_to (info); listen_to (warning); listen_to (fatal); old_receiver.hangup (); starting (); _active = true; theMain->run (); _active = false; stopping (); hangup (); return; } bool UI::running () { return _active; } void UI::kill () { if (_active) { pthread_kill (gui_thread, SIGKILL); } } void UI::quit () { UIRequest *req = get_request (Quit); if (req == 0) { return; } send_request (req); } static bool idle_quit () { Main::quit (); return true; } void UI::do_quit () { if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) { Main::quit (); } else { Glib::signal_idle().connect (sigc::ptr_fun (idle_quit)); } } void UI::touch_display (Touchable *display) { UIRequest *req = get_request (TouchDisplay); if (req == 0) { return; } req->display = display; send_request (req); } void UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp) { UIRequest *req = get_request (SetTip); if (req == 0) { return; } req->widget = w; req->msg = tip; req->msg2 = hlp; send_request (req); } void UI::set_state (Widget *w, StateType state) { UIRequest *req = get_request (StateChange); if (req == 0) { return; } req->new_state = state; req->widget = w; send_request (req); } void UI::idle_add (int (*func)(void *), void *arg) { UIRequest *req = get_request (AddIdle); if (req == 0) { return; } req->function = func; req->arg = arg; send_request (req); } /* END abstract_ui interfaces */ void UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond) { char buf[256]; /* flush (nonblocking) pipe */ while (read (fd, buf, 256) > 0); ((UI *) arg)->handle_ui_requests (); } void UI::do_request (UIRequest* req) { if (req->type == ErrorMessage) { process_error_message (req->chn, req->msg); free (const_cast(req->msg)); /* it was strdup'ed */ req->msg = 0; /* don't free it again in the destructor */ } else if (req->type == Quit) { do_quit (); } else if (req->type == CallSlot) { req->slot (); } else if (req->type == TouchDisplay) { req->display->touch (); if (req->display->delete_after_touch()) { delete req->display; } } else if (req->type == StateChange) { req->widget->set_state (req->new_state); } else if (req->type == SetTip) { /* XXX need to figure out how this works */ } else { error << "GtkUI: unknown request type " << (int) req->type << endmsg; } } /*====================================================================== Error Display ======================================================================*/ void UI::receive (Transmitter::Channel chn, const char *str) { if (caller_is_ui_thread()) { process_error_message (chn, str); } else { UIRequest* req = get_request (ErrorMessage); if (req == 0) { return; } req->chn = chn; req->msg = strdup (str); send_request (req); } } #define OLD_STYLE_ERRORS 1 void UI::process_error_message (Transmitter::Channel chn, const char *str) { RefPtr