1 /*
2  * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2006-2015 Tim Mayberry <mojofunk@gmail.com>
5  * Copyright (C) 2007-2011 David Robillard <d@drobilla.net>
6  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
7  * Copyright (C) 2013-2018 Robin Gareus <robin@gareus.org>
8  * Copyright (C) 2016-2018 Ben Loftis <ben@harrisonconsoles.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 #ifdef WAF_BUILD
26 #include "gtk2ardour-config.h"
27 #endif
28 
29 /* this file exists solely to break compilation dependencies that
30    would connect changes to the mixer or editor objects.
31 */
32 
33 #include <cstdio>
34 
35 #include "pbd/error.h"
36 
37 #include "ardour/session.h"
38 #include "ardour/lv2_plugin.h"
39 
40 #include "gtkmm2ext/bindings.h"
41 
42 #include "actions.h"
43 #include "ardour_message.h"
44 #include "ardour_ui.h"
45 #include "public_editor.h"
46 #include "meterbridge.h"
47 #include "luainstance.h"
48 #include "luawindow.h"
49 #include "mixer_ui.h"
50 #include "recorder_ui.h"
51 #include "keyboard.h"
52 #include "keyeditor.h"
53 #include "splash.h"
54 #include "rc_option_editor.h"
55 #include "route_params_ui.h"
56 #include "time_info_box.h"
57 #include "step_entry.h"
58 #include "opts.h"
59 #include "utils.h"
60 
61 #ifdef GDK_WINDOWING_X11
62 #include <gdk/gdkx.h>
63 #endif
64 
65 #include "pbd/i18n.h"
66 
67 using namespace Gtk;
68 using namespace PBD;
69 
70 namespace ARDOUR {
71 	class Session;
72 	class Route;
73 }
74 
75 using namespace ARDOUR;
76 using namespace Gtkmm2ext;
77 
78 void
we_have_dependents()79 ARDOUR_UI::we_have_dependents ()
80 {
81 	install_dependent_actions ();
82 
83 	/* The monitor section relies on at least 1 action defined by us. Since that
84 	 * action now exists, give it a chance to use it.
85 	 */
86 	mixer->monitor_section().use_others_actions ();
87 
88 	/* Create "static" actions that apply to all ProcessorBoxes
89 	 */
90 	ProcessorBox::register_actions ();
91 	StepEntry::setup_actions_and_bindings ();
92 
93 	/* Global, editor, mixer, processor box actions are defined now. Link
94 	   them with any bindings, so that GTK does not get a chance to define
95 	   the GTK accel map entries first when we ask the GtkUIManager to
96 	   create menus/widgets.
97 
98 	   If GTK adds the actions to its accel map before we do, we lose our
99 	   freedom to use any keys. More precisely, we can use any keys, but
100 	   ones that GTK considers illegal as accelerators will not show up in
101 	   menus.
102 
103 	   There are other dynamic actions that can be created by a monitor
104 	   section, by step entry dialogs. These need to be handled
105 	   separately. They don't tend to use GTK-illegal bindings and more
106 	   importantly they don't have menus showing the bindings, so it is
107 	   less of an issue.
108 	*/
109 
110 	Gtkmm2ext::Bindings::associate_all ();
111 
112 	editor->setup_tooltips ();
113 	editor->UpdateAllTransportClocks.connect (sigc::mem_fun (*this, &ARDOUR_UI::update_transport_clocks));
114 
115 	/* all actions are defined */
116 
117 	ActionManager::load_menus (ARDOUR_COMMAND_LINE::menus_file);
118 
119 	/* catch up on parameters */
120 
121 	boost::function<void (std::string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
122 	Config->map_parameters (pc);
123 
124 	UIConfiguration::instance().reset_dpi ();
125 }
126 
127 void
connect_dependents_to_session(ARDOUR::Session * s)128 ARDOUR_UI::connect_dependents_to_session (ARDOUR::Session *s)
129 {
130 	DisplaySuspender ds;
131 	BootMessage (_("Setup Editor"));
132 	editor->set_session (s);
133 	BootMessage (_("Setup Mixer"));
134 	mixer->set_session (s);
135 	recorder->set_session (s);
136 	meterbridge->set_session (s);
137 	luawindow->set_session (s);
138 
139 	/* its safe to do this now */
140 
141 	BootMessage (_("Reload Session History"));
142 	s->restore_history ("");
143 }
144 
145 /** The main editor window has been closed */
146 gint
exit_on_main_window_close(GdkEventAny *)147 ARDOUR_UI::exit_on_main_window_close (GdkEventAny * /*ev*/)
148 {
149 #ifdef __APPLE__
150 	/* just hide the window, and return - the top menu stays up */
151 	editor->hide ();
152 	return TRUE;
153 #else
154 	/* time to get out of here */
155 	finish();
156 	return TRUE;
157 #endif
158 }
159 
160 GtkNotebook*
tab_window_root_drop(GtkNotebook * src,GtkWidget * w,gint x,gint y,gpointer)161 ARDOUR_UI::tab_window_root_drop (GtkNotebook* src,
162 				 GtkWidget* w,
163 				 gint x,
164 				 gint y,
165 				 gpointer)
166 {
167 	using namespace std;
168 	Gtk::Notebook* nb = 0;
169 	Gtk::Window* win = 0;
170 	ArdourWidgets::Tabbable* tabbable = 0;
171 
172 
173 	if (w == GTK_WIDGET(editor->contents().gobj())) {
174 		tabbable = editor;
175 	} else if (w == GTK_WIDGET(mixer->contents().gobj())) {
176 		tabbable = mixer;
177 	} else if (w == GTK_WIDGET(rc_option_editor->contents().gobj())) {
178 		tabbable = rc_option_editor;
179 	} else if (w == GTK_WIDGET(recorder->contents().gobj())) {
180 		tabbable = recorder;
181 	} else {
182 		return 0;
183 	}
184 
185 	nb = tabbable->tab_root_drop ();
186 	win = tabbable->own_window ();
187 
188 	if (nb) {
189 		win->move (x, y);
190 		win->show_all ();
191 		win->present ();
192 		return nb->gobj();
193 	}
194 
195 	return 0; /* what was that? */
196 }
197 
198 bool
idle_ask_about_quit()199 ARDOUR_UI::idle_ask_about_quit ()
200 {
201 	if (_session && _session->dirty()) {
202 		finish ();
203 	} else {
204 		/* no session or session not dirty, but still ask anyway */
205 
206 		ArdourMessageDialog msg (string_compose (_("Quit %1?"), PROGRAM_NAME),
207 		                         false, /* no markup */
208 		                         Gtk::MESSAGE_INFO,
209 		                         Gtk::BUTTONS_YES_NO,
210 		                         true); /* modal */
211 		msg.set_default_response (Gtk::RESPONSE_YES);
212 
213 		if (msg.run() == Gtk::RESPONSE_YES) {
214 			finish ();
215 		}
216 	}
217 
218 	/* not reached but keep the compiler happy */
219 
220 	return false;
221 }
222 
223 bool
main_window_delete_event(GdkEventAny * ev)224 ARDOUR_UI::main_window_delete_event (GdkEventAny* ev)
225 {
226 	/* quit the application as soon as we go idle. If we call this here,
227 	 * the window manager/desktop can think we're taking too longer to
228 	 * handle the "delete" event
229 	 */
230 
231 	Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::idle_ask_about_quit));
232 
233 	return true;
234 }
235 
236 static GtkNotebook*
tab_window_root_drop(GtkNotebook * src,GtkWidget * w,gint x,gint y,gpointer user_data)237 tab_window_root_drop (GtkNotebook* src,
238                       GtkWidget* w,
239                       gint x,
240                       gint y,
241                       gpointer user_data)
242 {
243 	return ARDOUR_UI::instance()->tab_window_root_drop (src, w, x, y, user_data);
244 }
245 
246 int
setup_windows()247 ARDOUR_UI::setup_windows ()
248 {
249 	_tabs.set_show_border(false);
250 	_tabs.signal_switch_page().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_switch));
251 	_tabs.signal_page_added().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_added));
252 	_tabs.signal_page_removed().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_removed));
253 
254 	rc_option_editor = new RCOptionEditor;
255 	rc_option_editor->StateChange.connect (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_state_change));
256 
257 	if (create_editor ()) {
258 		error << _("UI: cannot setup editor") << endmsg;
259 		return -1;
260 	}
261 
262 	if (create_mixer ()) {
263 		error << _("UI: cannot setup mixer") << endmsg;
264 		return -1;
265 	}
266 
267 	if (create_recorder ()) {
268 		error << _("UI: cannot setup recorder") << endmsg;
269 		return -1;
270 	}
271 
272 	if (create_meterbridge ()) {
273 		error << _("UI: cannot setup meterbridge") << endmsg;
274 		return -1;
275 	}
276 
277 	if (create_luawindow ()) {
278 		error << _("UI: cannot setup luawindow") << endmsg;
279 		return -1;
280 	}
281 
282 	time_info_box = new TimeInfoBox ("ToolbarTimeInfo", false);
283 	/* all other dialogs are created conditionally */
284 
285 	we_have_dependents ();
286 
287 	/* order of addition affects order seen in initial window display */
288 
289 	rc_option_editor->add_to_notebook (_tabs);
290 	mixer->add_to_notebook (_tabs);
291 	editor->add_to_notebook (_tabs);
292 	recorder->add_to_notebook (_tabs);
293 
294 	top_packer.pack_start (menu_bar_base, false, false);
295 
296 	main_vpacker.pack_start (top_packer, false, false);
297 
298 	ArdourWidgets::ArdourDropShadow *spacer = manage (new (ArdourWidgets::ArdourDropShadow));
299 	spacer->set_size_request( -1, 4 );
300 	spacer->show();
301 
302 	/* now add the transport sample to the top of main window */
303 
304 	main_vpacker.pack_start ( *spacer, false, false);
305 	main_vpacker.pack_start (transport_frame, false, false);
306 	main_vpacker.pack_start (_tabs, true, true);
307 
308 	LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::action_script_changed));
309 
310 	for (int i = 0; i < MAX_LUA_ACTION_BUTTONS; ++i) {
311 		std::string const a = string_compose (X_("script-%1"), i + 1);
312 		Glib::RefPtr<Action> act = ActionManager::get_action(X_("LuaAction"), a.c_str());
313 		assert (act);
314 		action_script_call_btn[i].set_name ("lua action button");
315 		action_script_call_btn[i].set_text (string_compose ("%1%2", std::hex, i+1));
316 		action_script_call_btn[i].set_related_action (act);
317 		action_script_call_btn[i].signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::bind_lua_action_script), i), false);
318 		if (act->get_sensitive ()) {
319 			action_script_call_btn[i].set_visual_state (Gtkmm2ext::VisualState (action_script_call_btn[i].visual_state() & ~Gtkmm2ext::Insensitive));
320 		} else {
321 			action_script_call_btn[i].set_visual_state (Gtkmm2ext::VisualState (action_script_call_btn[i].visual_state() | Gtkmm2ext::Insensitive));
322 		}
323 		action_script_call_btn[i].set_sizing_text ("88");
324 		action_script_call_btn[i].set_no_show_all ();
325 	}
326 
327 	setup_transport();
328 	build_menu_bar ();
329 	setup_tooltips ();
330 
331 	_main_window.signal_delete_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::main_window_delete_event));
332 
333 	/* pack the main vpacker into the main window and show everything
334 	 */
335 
336 	_main_window.add (main_vpacker);
337 	transport_frame.show_all ();
338 
339 	const XMLNode* mnode = main_window_settings ();
340 
341 	if (mnode) {
342 		XMLProperty const * prop;
343 		gint x = -1;
344 		gint y = -1;
345 		gint w = -1;
346 		gint h = -1;
347 
348 		if ((prop = mnode->property (X_("x"))) != 0) {
349 			x = atoi (prop->value());
350 		}
351 
352 		if ((prop = mnode->property (X_("y"))) != 0) {
353 			y = atoi (prop->value());
354 		}
355 
356 		if ((prop = mnode->property (X_("w"))) != 0) {
357 			w = atoi (prop->value());
358 		}
359 
360 		if ((prop = mnode->property (X_("h"))) != 0) {
361 			h = atoi (prop->value());
362 		}
363 
364 		if (x >= 0 && y >= 0 && w >= 0 && h >= 0) {
365 			_main_window.set_position (Gtk::WIN_POS_NONE);
366 		}
367 
368 		if (x >= 0 && y >= 0) {
369 			_main_window.move (x, y);
370 		}
371 
372 		if (w > 0 && h > 0) {
373 			_main_window.set_default_size (w, h);
374 		}
375 
376 		std::string current_tab;
377 
378 		if ((prop = mnode->property (X_("current-tab"))) != 0) {
379 			current_tab = prop->value();
380 		} else {
381 			current_tab = "editor";
382 		}
383 		if (mixer && current_tab == "mixer") {
384 			_tabs.set_current_page (_tabs.page_num (mixer->contents()));
385 		} else if (rc_option_editor && current_tab == "preferences") {
386 			_tabs.set_current_page (_tabs.page_num (rc_option_editor->contents()));
387 		} else if (recorder && current_tab == "recorder") {
388 			_tabs.set_current_page (_tabs.page_num (recorder->contents()));
389 		} else if (editor) {
390 			_tabs.set_current_page (_tabs.page_num (editor->contents()));
391 		}
392 	}
393 
394 	setup_toplevel_window (_main_window, "", this);
395 	_main_window.show_all ();
396 
397 	_tabs.set_show_tabs (false);
398 
399 	/* It would be nice if Gtkmm had wrapped this rather than just
400 	 * deprecating the old set_window_creation_hook() method, but oh well...
401 	 */
402 	g_signal_connect (_tabs.gobj(), "create-window", (GCallback) ::tab_window_root_drop, this);
403 
404 #ifdef GDK_WINDOWING_X11
405 	/* allow externalUIs to be transient, on top of the main window */
406 	LV2Plugin::set_main_window_id (GDK_DRAWABLE_XID(_main_window.get_window()->gobj()));
407 #endif
408 
409 	return 0;
410 }
411 
412 bool
bind_lua_action_script(GdkEventButton * ev,int i)413 ARDOUR_UI::bind_lua_action_script (GdkEventButton*ev, int i)
414 {
415 	if (!_session) {
416 		return false;
417 	}
418 	LuaInstance *li = LuaInstance::instance();
419 	std::string name;
420 	if (ev->button != 3 && !(ev->button == 1 && !li->lua_action_name (i, name))) {
421 		return false;
422 	}
423 	if (Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::TertiaryModifier)) {
424 		li->remove_lua_action (i);
425 	} else {
426 		li->interactive_add (LuaScriptInfo::EditorAction, i);
427 	}
428 	return true;
429 }
430 
431 void
action_script_changed(int i,const std::string & n)432 ARDOUR_UI::action_script_changed (int i, const std::string& n)
433 {
434 	if (i < 0 || i >= MAX_LUA_ACTION_SCRIPTS) {
435 		return;
436 	}
437 
438 	if (i < MAX_LUA_ACTION_BUTTONS) {
439 		if (LuaInstance::instance()->lua_action_has_icon (i)) {
440 			uintptr_t ii = i;
441 			action_script_call_btn[i].set_icon (&LuaInstance::render_action_icon, (void*)ii);
442 		} else {
443 			action_script_call_btn[i].set_icon (0, 0);
444 		}
445 		if (n.empty ()) {
446 			action_script_call_btn[i].set_text (string_compose ("%1%2", std::hex, i+1));
447 		} else {
448 			action_script_call_btn[i].set_text (n.substr(0,1));
449 		}
450 	}
451 
452 	std::string const a = string_compose (X_("script-%1"), i + 1);
453 	Glib::RefPtr<Action> act = ActionManager::get_action(X_("LuaAction"), a.c_str());
454 	assert (act);
455 	if (n.empty ()) {
456 		act->set_label (string_compose (_("Unset #%1"), i + 1));
457 		act->set_tooltip (_("No action bound\nRight-click to assign"));
458 		act->set_sensitive (false);
459 	} else {
460 		act->set_label (n);
461 		act->set_tooltip (string_compose (_("%1\n\nClick to run\nRight-click to re-assign\nShift+right-click to unassign"), n));
462 		act->set_sensitive (true);
463 	}
464 	KeyEditor::UpdateBindings ();
465 }
466