remove use of implicit namespaces
[ladish.git] / gui / Patchage.cpp
blobb2f8339a9c369552db9d2e7a7ac9c5fcc382af9d
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008, 2009 Nedko Arnaudov <nedko@arnaudov.name>
6 * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
8 **************************************************************************
9 * This file contains implementation of the application class
10 **************************************************************************
12 * LADI Session Handler is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * LADI Session Handler is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
24 * or write to the Free Software Foundation, Inc.,
25 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "common.h"
30 #include <string.h>
31 #include <cmath>
32 #include <sstream>
33 #include <fstream>
34 #include <libgnomecanvasmm.h>
35 #include <libglademm/xml.h>
36 #include <gtk/gtkwindow.h>
37 #include <boost/format.hpp>
39 #include "jack_proxy.hpp"
40 #include "Patchage.hpp"
41 #include "PatchageCanvas.hpp"
42 #include "StateManager.hpp"
43 #include "lash_proxy.hpp"
44 #include "project_list.hpp"
45 #include "session.hpp"
46 #include "globals.hpp"
47 #include "a2j_proxy.hpp"
48 #include "PatchageModule.hpp"
49 #include "dbus_helpers.h"
50 #include "load_projects_dialog.hpp"
52 Patchage * g_app;
54 #define LOG_TO_STD
55 //#define LOG_TO_STATUS
57 /* Gtk helpers (resize combo boxes) */
59 static void
60 gtkmm_get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
61 int& width,
62 int& height)
64 Pango::Rectangle ink_rect = layout->get_ink_extents ();
66 width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
67 height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
70 static void
71 gtkmm_set_width_for_given_text (Gtk::Widget &w, const gchar *text,
72 gint hpadding/*, gint vpadding*/)
75 int old_width, old_height;
76 w.get_size_request(old_width, old_height);
78 int width, height;
79 w.ensure_style ();
81 gtkmm_get_ink_pixel_size (w.create_pango_layout (text), width, height);
82 w.set_size_request(width + hpadding, old_height);//height + vpadding);
85 /* end Gtk helpers */
88 #define INIT_WIDGET(x) x(g_xml, ((const char*)#x) + 1)
90 Patchage::Patchage(int argc, char** argv)
91 : _max_dsp_load(0.0)
92 , INIT_WIDGET(_about_win)
93 , INIT_WIDGET(_buffer_size_combo)
94 , INIT_WIDGET(_clear_load_but)
95 , INIT_WIDGET(_main_scrolledwin)
96 , INIT_WIDGET(_main_win)
97 , INIT_WIDGET(_main_xrun_progress)
98 , INIT_WIDGET(_main_a2j_status_label)
99 , INIT_WIDGET(_menu_file_quit)
100 , INIT_WIDGET(_menu_help_about)
101 , INIT_WIDGET(_menu_jack_start)
102 , INIT_WIDGET(_menu_jack_stop)
103 , INIT_WIDGET(_menu_a2j_start)
104 , INIT_WIDGET(_menu_a2j_stop)
105 , INIT_WIDGET(_menu_load_project)
106 , INIT_WIDGET(_menu_save_all_projects)
107 , INIT_WIDGET(_menu_close_all_projects)
108 , INIT_WIDGET(_menu_store_positions)
109 , INIT_WIDGET(_menu_view_arrange)
110 , INIT_WIDGET(_menu_view_projects)
111 , INIT_WIDGET(_menu_view_refresh)
112 , INIT_WIDGET(_menu_view_toolbar)
113 , INIT_WIDGET(_project_list_viewport)
114 , INIT_WIDGET(_sample_rate_label)
115 , INIT_WIDGET(_toolbar)
116 , INIT_WIDGET(_zoom_full_but)
117 , INIT_WIDGET(_zoom_normal_but)
119 g_app = this;
121 _settings_filename = getenv("HOME");
122 _settings_filename += "/.gladishrc";
123 _state_manager = new StateManager();
124 //_canvas = boost::shared_ptr<PatchageCanvas>(new PatchageCanvas(this, 1600*2, 1200*2));
126 while (argc > 0) {
127 if (!strcmp(*argv, "--help")) {
128 std::cout << "Usage: patchage [OPTIONS]\nOptions: --no-alsa" << std::endl;
129 exit(0);
132 argv++;
133 argc--;
136 patchage_dbus_init();
138 Glib::set_application_name("Patchage");
139 _about_win->property_program_name() = "Patchage";
140 _about_win->property_logo_icon_name() = "gladish";
141 gtk_window_set_default_icon_name("gladish");
143 gtkmm_set_width_for_given_text(*_buffer_size_combo, "4096 frames", 40);
145 // _main_scrolledwin->add(*_canvas);
146 // _canvas->scroll_to(static_cast<int>(_canvas->width()/2 - 320),
147 // static_cast<int>(_canvas->height()/2 - 240)); // FIXME: hardcoded
149 _main_scrolledwin->property_hadjustment().get_value()->set_step_increment(10);
150 _main_scrolledwin->property_vadjustment().get_value()->set_step_increment(10);
152 _main_scrolledwin->signal_scroll_event().connect(
153 sigc::mem_fun(this, &Patchage::on_scroll));
155 _buffer_size_combo->signal_changed().connect(
156 sigc::mem_fun(this, &Patchage::buffer_size_changed));
157 _clear_load_but->signal_clicked().connect(
158 sigc::mem_fun(this, &Patchage::clear_load));
159 _zoom_normal_but->signal_clicked().connect(sigc::bind(
160 sigc::mem_fun(this, &Patchage::zoom), 1.0));
161 // _zoom_full_but->signal_clicked().connect(
162 // sigc::mem_fun(_canvas.get(), &PatchageCanvas::zoom_full));
164 _menu_load_project->signal_activate().connect(
165 sigc::mem_fun(this, &Patchage::load_project_ask));
166 _menu_save_all_projects->signal_activate().connect(
167 sigc::mem_fun(this, &Patchage::save_all_projects));
168 _menu_close_all_projects->signal_activate().connect(
169 sigc::mem_fun(this, &Patchage::close_all_projects));
171 _menu_store_positions->signal_activate().connect(
172 sigc::mem_fun(this, &Patchage::on_store_positions));
173 _menu_file_quit->signal_activate().connect(
174 sigc::mem_fun(this, &Patchage::on_quit));
175 _menu_view_refresh->signal_activate().connect(
176 sigc::mem_fun(this, &Patchage::refresh));
177 _menu_view_arrange->signal_activate().connect(
178 sigc::mem_fun(this, &Patchage::on_arrange));
179 _menu_view_toolbar->signal_activate().connect(
180 sigc::mem_fun(this, &Patchage::on_view_toolbar));
181 _menu_view_projects->signal_toggled().connect(
182 sigc::mem_fun(this, &Patchage::on_show_projects));
183 _menu_help_about->signal_activate().connect(
184 sigc::mem_fun(this, &Patchage::on_help_about));
186 // _canvas->show();
187 _main_win->present();
189 _state_manager->load(_settings_filename);
191 _main_win->resize(
192 static_cast<int>(_state_manager->get_window_size().x),
193 static_cast<int>(_state_manager->get_window_size().y));
195 _main_win->move(
196 static_cast<int>(_state_manager->get_window_location().x),
197 static_cast<int>(_state_manager->get_window_location().y));
199 _about_win->set_transient_for(*_main_win);
201 _a2j = new a2j_proxy;
203 //info_msg(str(boost::format("a2j jack client name is '%s'") % _a2j->get_jack_client_name()));
205 _session = new session();
207 _project_list = new project_list(this, _session);
208 _project_list_viewport->hide();
210 _lash = new lash_proxy(_session);
212 _jack = new jack_proxy(this);
214 _menu_jack_start->signal_activate().connect(
215 sigc::mem_fun(_jack, &jack_proxy::start_server));
216 _menu_jack_stop->signal_activate().connect(
217 sigc::mem_fun(_jack, &jack_proxy::stop_server));
219 _menu_a2j_start->signal_activate().connect(
220 sigc::mem_fun(_a2j, &a2j_proxy::start_bridge));
221 _menu_a2j_stop->signal_activate().connect(
222 sigc::mem_fun(_a2j, &a2j_proxy::stop_bridge));
224 jack_status_changed(_jack->is_started());
226 connect_widgets();
227 update_state();
229 // _canvas->grab_focus();
231 // Idle callback, check if we need to refresh
232 Glib::signal_timeout().connect(
233 sigc::mem_fun(this, &Patchage::idle_callback), 100);
236 Patchage::~Patchage()
238 delete _jack;
239 delete _lash;
240 delete _project_list;
241 delete _session;
242 delete _state_manager;
243 delete _a2j;
245 _about_win.destroy();
246 //_main_win.destroy();
248 patchage_dbus_uninit();
252 bool
253 Patchage::idle_callback()
255 update_load();
257 return true;
261 void
262 Patchage::update_toolbar()
264 bool started;
266 started = _jack->is_started();
268 _buffer_size_combo->set_sensitive(started);
270 if (started)
272 _buffer_size_combo->set_active((int)log2f(_jack->buffer_size()) - 5);
277 void
278 Patchage::update_load()
280 if (!_jack->is_started())
282 _main_xrun_progress->set_text("JACK stopped");
283 return;
286 char tmp_buf[8];
287 snprintf(tmp_buf, 8, "%zd", _jack->xruns());
289 _main_xrun_progress->set_text(std::string(tmp_buf) + " Dropouts");
291 float load = _jack->get_dsp_load();
293 load /= 100.0; // dbus returns it in percents, we use 0..1
295 if (load > _max_dsp_load)
297 _max_dsp_load = load;
298 _main_xrun_progress->set_fraction(load);
303 void
304 Patchage::zoom(double z)
306 _state_manager->set_zoom(z);
307 // _canvas->set_zoom(z);
311 void
312 Patchage::refresh()
314 // assert(_canvas);
316 // _canvas->destroy();
318 // if (_jack)
319 // _jack->refresh();
321 // for (ItemList::iterator i = _canvas->items().begin(); i != _canvas->items().end(); ++i) {
322 // (*i)->resize();
323 // }
327 /** Update the stored window location and size in the StateManager (in memory).
329 void
330 Patchage::store_window_location()
332 int loc_x, loc_y, size_x, size_y;
333 _main_win->get_position(loc_x, loc_y);
334 _main_win->get_size(size_x, size_y);
335 Coord window_location;
336 window_location.x = loc_x;
337 window_location.y = loc_y;
338 Coord window_size;
339 window_size.x = size_x;
340 window_size.y = size_y;
341 _state_manager->set_window_location(window_location);
342 _state_manager->set_window_size(window_size);
346 void
347 Patchage::clear_load()
349 _main_xrun_progress->set_fraction(0.0);
350 _jack->reset_xruns();
351 _max_dsp_load = 0.0;
355 void
356 Patchage::error_msg(const std::string& msg)
358 #if defined(LOG_TO_STATUS)
359 status_msg(msg);
360 #endif
361 #if defined(LOG_TO_STD)
362 std::cerr << msg << std::endl;
363 #endif
367 void
368 Patchage::info_msg(const std::string& msg)
370 #if defined(LOG_TO_STATUS)
371 status_msg(msg);
372 #endif
373 #if defined(LOG_TO_STD)
374 std::cerr << msg << std::endl;
375 #endif
379 void
380 Patchage::status_msg(const std::string& msg)
385 void
386 Patchage::update_state()
388 // for (ItemList::iterator i = _canvas->items().begin(); i != _canvas->items().end(); ++i) {
389 // shared_ptr<Module> module = dynamic_pointer_cast<Module>(*i);
390 // if (module)
391 // module->load_location();
392 // }
396 /** Update the sensitivity status of menus to reflect the present.
398 * (eg. disable "Connect to Jack" when Patchage is already connected to Jack)
400 void
401 Patchage::connect_widgets()
403 _jack->signal_started.connect(
404 sigc::bind(sigc::mem_fun(this, &Patchage::jack_status_changed), true));
406 _jack->signal_stopped.connect(
407 sigc::bind(sigc::mem_fun(this, &Patchage::jack_status_changed), false));
410 void
411 Patchage::jack_status_changed(
412 bool started)
414 update_toolbar();
416 _menu_jack_start->set_sensitive(!started);
417 _menu_jack_stop->set_sensitive(started);
418 _clear_load_but->set_sensitive(started);
419 if (!started)
421 _main_xrun_progress->set_fraction(0.0);
425 void
426 Patchage::on_arrange()
428 // assert(_canvas);
430 // _canvas->arrange();
434 void
435 Patchage::on_help_about()
437 _about_win->run();
438 _about_win->hide();
442 void
443 Patchage::on_quit()
445 _main_win->hide();
448 void
449 Patchage::on_show_projects()
451 if (_menu_view_projects->get_active())
452 _project_list_viewport->show();
453 else
454 _project_list_viewport->hide();
458 void
459 Patchage::on_store_positions()
461 store_window_location();
462 _state_manager->save(_settings_filename);
466 void
467 Patchage::on_view_toolbar()
469 if (_menu_view_toolbar->get_active())
470 _toolbar->show();
471 else
472 _toolbar->hide();
476 bool
477 Patchage::on_scroll(GdkEventScroll* ev)
479 std::cout << "ON SCROLL" << std::endl;
480 return false;
484 void
485 Patchage::buffer_size_changed()
487 const int selected = _buffer_size_combo->get_active_row_number();
489 if (selected == -1) {
490 update_toolbar();
491 } else {
492 uint32_t buffer_size = 1 << (selected+5);
494 // this check is temporal workaround for jack bug
495 // we skip setting buffer size if it same as acutal one
496 // proper place for such check is in jack
497 if (_jack->buffer_size() != buffer_size)
499 if (!_jack->set_buffer_size(buffer_size))
501 update_toolbar(); // reset combo box to actual value
507 void
508 Patchage::set_studio_availability(
509 bool available)
511 if (available)
513 _main_win->set_title("Active Studio - LADI Session Handler");
515 else
517 _main_win->set_title("LADI Session Handler");
519 _project_list->set_lash_availability(available);
520 _menu_view_projects->set_active(available);
523 void
524 Patchage::set_a2j_status(
525 unsigned int status)
527 const char * status_text;
529 switch (status)
531 case A2J_STATUS_NO_RESPONSE:
532 status_text = "A2J N/A";
533 _menu_a2j_start->set_sensitive(false);
534 _menu_a2j_stop->set_sensitive(false);
535 break;
536 case A2J_STATUS_BRIDGE_STOPPED:
537 status_text = "A2J bridge stopped";
538 _menu_a2j_start->set_sensitive(true);
539 _menu_a2j_stop->set_sensitive(false);
540 break;
541 case A2J_STATUS_BRIDGE_STARTED:
542 status_text = "A2J bridge started";
543 _menu_a2j_start->set_sensitive(false);
544 _menu_a2j_stop->set_sensitive(true);
545 break;
546 default:
547 error_msg(str(boost::format("Unknown A2J status %u") % status));
548 status_text = "Unknown A2J status";
549 _menu_a2j_start->set_sensitive(true);
550 _menu_a2j_stop->set_sensitive(true);
551 break;
554 _main_a2j_status_label->set_text(status_text);
557 void
558 Patchage::load_project_ask()
560 std::list<lash_project_info> projects;
562 _lash->get_available_projects(projects);
563 run_load_project_dialog(projects);
566 void
567 Patchage::load_project(
568 const std::string& project_name)
570 _lash->load_project(project_name);
573 void
574 Patchage::save_all_projects()
576 _lash->save_all_projects();
579 void
580 Patchage::save_project(
581 const std::string& project_name)
583 _lash->save_project(project_name);
586 void
587 Patchage::close_project(
588 const std::string& project_name)
590 _lash->close_project(project_name);
593 void
594 Patchage::close_all_projects()
596 _lash->close_all_projects();
599 void
600 Patchage::on_port_added(
601 const char * jack_client_name,
602 const char * jack_port_name,
603 PortType port_type,
604 bool is_input,
605 bool is_terminal)
607 #if 0
608 bool is_a2j_mapped;
609 std::string canvas_client_name;
610 std::string canvas_port_name;
611 uint32_t alsa_client_id;
612 boost::shared_ptr<PatchageModule> module;
614 is_a2j_mapped = strcmp(_a2j->get_jack_client_name(), jack_client_name) == 0;
615 if (is_a2j_mapped)
617 if (!_a2j->map_jack_port(jack_port_name, canvas_client_name, canvas_port_name, alsa_client_id))
619 return;
622 canvas_port_name = str(boost::format(canvas_port_name + " [a2j:%u]") % alsa_client_id);
624 else
626 canvas_client_name = jack_client_name;
627 canvas_port_name = jack_port_name;
630 ModuleType module_type = InputOutput;
631 if (_state_manager->get_module_split(canvas_client_name, is_terminal && !is_a2j_mapped)) {
632 if (is_input) {
633 module_type = Input;
634 } else {
635 module_type = Output;
639 module = _canvas->find_module(canvas_client_name, module_type);
640 if (!module) {
641 module = boost::shared_ptr<PatchageModule>(new PatchageModule(this, canvas_client_name, module_type));
642 module->load_location();
643 _canvas->add_item(module);
646 if (module->get_port(canvas_port_name)) {
647 return;
650 boost::shared_ptr<PatchagePort> port = boost::shared_ptr<PatchagePort>(
651 new PatchagePort(
652 module,
653 canvas_port_name,
654 is_input,
655 _state_manager->get_port_color(port_type)));
657 port->type = port_type;
658 port->is_a2j_mapped = is_a2j_mapped;
659 if (is_a2j_mapped)
661 port->a2j_jack_port_name = jack_port_name;
664 module->add_port(port);
666 module->resize();
667 #endif
670 #if 0
671 boost::shared_ptr<PatchagePort>
672 Patchage::lookup_port(
673 const char * jack_client_name,
674 const char * jack_port_name)
676 if (strcmp(_a2j->get_jack_client_name(), jack_client_name) == 0)
678 return _canvas->lookup_port_by_a2j_jack_port_name(jack_port_name);
681 return _canvas->get_port(jack_client_name, jack_port_name);
683 #endif
685 void
686 Patchage::on_port_removed(
687 const char * jack_client_name,
688 const char * jack_port_name)
690 #if 0
691 boost::shared_ptr<PatchagePort> port = lookup_port(jack_client_name, jack_port_name);
692 if (!port) {
693 error_msg(str(boost::format("Unable to remove unknown port '%s':'%s'") % jack_client_name % jack_port_name));
694 return;
697 boost::shared_ptr<PatchageModule> module = dynamic_pointer_cast<PatchageModule>(port->module().lock());
699 module->remove_port(port);
700 port.reset();
702 // No empty modules (for now)
703 if (module->num_ports() == 0) {
704 _canvas->remove_item(module);
705 module.reset();
706 } else {
707 module->resize();
709 #endif
712 void
713 Patchage::on_ports_connected(
714 const char * jack_client1_name,
715 const char * jack_port1_name,
716 const char * jack_client2_name,
717 const char * jack_port2_name)
719 #if 0
720 boost::shared_ptr<PatchagePort> port1 = lookup_port(jack_client1_name, jack_port1_name);
721 if (!port1) {
722 error_msg((std::string)"Unable to connect unknown port '" + jack_port1_name + "' of client '" + jack_client1_name + "'");
723 return;
726 boost::shared_ptr<PatchagePort> port2 = lookup_port(jack_client2_name, jack_port2_name);
727 if (!port2) {
728 error_msg((std::string)"Unable to connect unknown port '" + jack_port2_name + "' of client '" + jack_client2_name + "'");
729 return;
732 _canvas->add_connection(port1, port2, port1->color() + 0x22222200);
733 #endif
736 void
737 Patchage::on_ports_disconnected(
738 const char * jack_client1_name,
739 const char * jack_port1_name,
740 const char * jack_client2_name,
741 const char * jack_port2_name)
743 #if 0
744 boost::shared_ptr<PatchagePort> port1 = lookup_port(jack_client1_name, jack_port1_name);
745 if (!port1) {
746 error_msg((std::string)"Unable to disconnect unknown port '" + jack_port1_name + "' of client '" + jack_client1_name + "'");
747 return;
750 boost::shared_ptr<PatchagePort> port2 = lookup_port(jack_client2_name, jack_port2_name);
751 if (!port2) {
752 error_msg((std::string)"Unable to disconnect unknown port '" + jack_port2_name + "' of client '" + jack_client2_name + "'");
753 return;
756 _canvas->remove_connection(port1, port2);
757 #endif
760 #if 0
761 static
762 bool
763 port_type_match(
764 boost::shared_ptr<PatchagePort> port1,
765 boost::shared_ptr<PatchagePort> port2)
767 return port1->type == port2->type;
769 #endif
771 void
772 Patchage::get_port_jack_names(
773 boost::shared_ptr<PatchagePort> port,
774 std::string& jack_client_name,
775 std::string& jack_port_name)
777 #if 0
778 if (port->is_a2j_mapped)
780 jack_client_name = _a2j->get_jack_client_name();
781 jack_port_name = port->a2j_jack_port_name;
783 else
785 jack_client_name = port->module().lock()->name();
786 jack_port_name = port->name();
788 #endif
791 void
792 Patchage::connect(
793 boost::shared_ptr<PatchagePort> port1,
794 boost::shared_ptr<PatchagePort> port2)
796 #if 0
797 std::string jack_client1_name;
798 std::string jack_port1_name;
799 std::string jack_client2_name;
800 std::string jack_port2_name;
802 if (port_type_match(port1, port2))
804 get_port_jack_names(port1, jack_client1_name, jack_port1_name);
805 get_port_jack_names(port2, jack_client2_name, jack_port2_name);
807 _jack->connect(
808 jack_client1_name.c_str(),
809 jack_port1_name.c_str(),
810 jack_client2_name.c_str(),
811 jack_port2_name.c_str());
813 else
815 status_msg("ERROR: Attempt to connect ports with mismatched types");
817 #endif
820 void
821 Patchage::disconnect(
822 boost::shared_ptr<PatchagePort> port1,
823 boost::shared_ptr<PatchagePort> port2)
825 #if 0
826 std::string jack_client1_name;
827 std::string jack_port1_name;
828 std::string jack_client2_name;
829 std::string jack_port2_name;
831 if (port_type_match(port1, port2))
833 get_port_jack_names(port1, jack_client1_name, jack_port1_name);
834 get_port_jack_names(port2, jack_client2_name, jack_port2_name);
836 _jack->disconnect(
837 jack_client1_name.c_str(),
838 jack_port1_name.c_str(),
839 jack_client2_name.c_str(),
840 jack_port2_name.c_str());
842 else
844 status_msg("ERROR: Attempt to disconnect ports with mismatched types");
846 #endif
849 /** Destroy all JACK (canvas) ports.
851 void
852 Patchage::clear_canvas()
854 #if 0
855 ItemList modules = _canvas->items(); // copy
856 for (ItemList::iterator m = modules.begin(); m != modules.end(); ++m) {
857 shared_ptr<Module> module = dynamic_pointer_cast<Module>(*m);
858 if (!module)
859 continue;
861 PortVector ports = module->ports(); // copy
862 for (PortVector::iterator p = ports.begin(); p != ports.end(); ++p) {
863 boost::shared_ptr<PatchagePort> port = boost::dynamic_pointer_cast<PatchagePort>(*p);
864 if (port)
866 module->remove_port(port);
867 port->hide();
871 if (module->ports().empty())
872 _canvas->remove_item(module);
873 else
874 module->resize();
876 #endif
879 bool
880 Patchage::is_canvas_empty()
882 // return _canvas->items().empty();
883 return false;