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