Allow Lua action scripts to provide a button icon
[ardour.git] / gtk2_ardour / ardour_ui_dependents.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 /* this file exists solely to break compilation dependencies that
25    would connect changes to the mixer or editor objects.
26 */
27
28 #include <cstdio>
29
30 #include "pbd/error.h"
31
32 #include "ardour/session.h"
33
34 #include "gtkmm2ext/bindings.h"
35
36 #include "actions.h"
37 #include "ardour_ui.h"
38 #include "public_editor.h"
39 #include "meterbridge.h"
40 #include "luainstance.h"
41 #include "luawindow.h"
42 #include "mixer_ui.h"
43 #include "keyboard.h"
44 #include "splash.h"
45 #include "rc_option_editor.h"
46 #include "route_params_ui.h"
47 #include "time_info_box.h"
48 #include "opts.h"
49 #include "utils.h"
50
51 #include "pbd/i18n.h"
52
53 using namespace Gtk;
54 using namespace PBD;
55
56 namespace ARDOUR {
57         class Session;
58         class Route;
59 }
60
61 using namespace ARDOUR;
62
63 void
64 ARDOUR_UI::we_have_dependents ()
65 {
66         install_actions ();
67         load_bindings ();
68
69         ProcessorBox::register_actions ();
70
71         /* Global, editor, mixer, processor box actions are defined now. Link
72            them with any bindings, so that GTK does not get a chance to define
73            the GTK accel map entries first when we ask the GtkUIManager to
74            create menus/widgets.
75
76            If GTK adds the actions to its accel map before we do, we lose our
77            freedom to use any keys. More precisely, we can use any keys, but
78            ones that GTK considers illegal as accelerators will not show up in
79            menus.
80
81            There are other dynamic actions that can be created by a monitor
82            section, by step entry dialogs. These need to be handled
83            separately. They don't tend to use GTK-illegal bindings and more
84            importantly they don't have menus showing the bindings, so it is
85            less of an issue.
86         */
87
88         Gtkmm2ext::Bindings::associate_all ();
89
90         editor->setup_tooltips ();
91         editor->UpdateAllTransportClocks.connect (sigc::mem_fun (*this, &ARDOUR_UI::update_transport_clocks));
92
93         /* catch up on tabbable state, in the right order to leave the editor
94          * selected by default
95          */
96
97         tabbable_state_change (*rc_option_editor);
98         tabbable_state_change (*mixer);
99         tabbable_state_change (*editor);
100
101         /* all actions are defined */
102
103         ActionManager::load_menus (ARDOUR_COMMAND_LINE::menus_file);
104
105         /* catch up on parameters */
106
107         boost::function<void (std::string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
108         Config->map_parameters (pc);
109
110         UIConfiguration::instance().reset_dpi ();
111 }
112
113 void
114 ARDOUR_UI::connect_dependents_to_session (ARDOUR::Session *s)
115 {
116         DisplaySuspender ds;
117         BootMessage (_("Setup Editor"));
118         editor->set_session (s);
119         BootMessage (_("Setup Mixer"));
120         mixer->set_session (s);
121         meterbridge->set_session (s);
122         luawindow->set_session (s);
123
124         /* its safe to do this now */
125
126         BootMessage (_("Reload Session History"));
127         s->restore_history ("");
128 }
129
130 /** The main editor window has been closed */
131 gint
132 ARDOUR_UI::exit_on_main_window_close (GdkEventAny * /*ev*/)
133 {
134 #ifdef TOP_MENUBAR
135         /* just hide the window, and return - the top menu stays up */
136         editor->hide ();
137         return TRUE;
138 #else
139         /* time to get out of here */
140         finish();
141         return TRUE;
142 #endif
143 }
144
145 GtkNotebook*
146 ARDOUR_UI::tab_window_root_drop (GtkNotebook* src,
147                                  GtkWidget* w,
148                                  gint x,
149                                  gint y,
150                                  gpointer)
151 {
152         using namespace std;
153         Gtk::Notebook* nb = 0;
154         Gtk::Window* win = 0;
155         Gtkmm2ext::Tabbable* tabbable = 0;
156
157
158         if (w == GTK_WIDGET(editor->contents().gobj())) {
159                 tabbable = editor;
160         } else if (w == GTK_WIDGET(mixer->contents().gobj())) {
161                 tabbable = mixer;
162         } else if (w == GTK_WIDGET(rc_option_editor->contents().gobj())) {
163                 tabbable = rc_option_editor;
164         } else {
165                 return 0;
166         }
167
168         nb = tabbable->tab_root_drop ();
169         win = tabbable->own_window ();
170
171         if (nb) {
172                 win->move (x, y);
173                 win->show_all ();
174                 win->present ();
175                 return nb->gobj();
176         }
177
178         return 0; /* what was that? */
179 }
180
181 bool
182 ARDOUR_UI::idle_ask_about_quit ()
183 {
184         if (_session && _session->dirty()) {
185                 finish ();
186         } else {
187                 /* no session or session not dirty, but still ask anyway */
188
189                 Gtk::MessageDialog msg (string_compose (_("Quit %1?"), PROGRAM_NAME),
190                                         false, /* no markup */
191                                         Gtk::MESSAGE_INFO,
192                                         Gtk::BUTTONS_YES_NO,
193                                         true); /* modal */
194                 msg.set_default_response (Gtk::RESPONSE_YES);
195
196                 if (msg.run() == Gtk::RESPONSE_YES) {
197                         finish ();
198                 }
199         }
200
201         /* not reached but keep the compiler happy */
202
203         return false;
204 }
205
206 bool
207 ARDOUR_UI::main_window_delete_event (GdkEventAny* ev)
208 {
209         /* quit the application as soon as we go idle. If we call this here,
210          * the window manager/desktop can think we're taking too longer to
211          * handle the "delete" event
212          */
213
214         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::idle_ask_about_quit));
215
216         return true;
217 }
218
219 static GtkNotebook*
220 tab_window_root_drop (GtkNotebook* src,
221                       GtkWidget* w,
222                       gint x,
223                       gint y,
224                       gpointer user_data)
225 {
226         return ARDOUR_UI::instance()->tab_window_root_drop (src, w, x, y, user_data);
227 }
228
229 int
230 ARDOUR_UI::setup_windows ()
231 {
232         /* actions do not need to be defined when we load keybindings. They
233          * will be lazily discovered. But bindings do need to exist when we
234          * create windows/tabs with their own binding sets.
235          */
236
237         keyboard->setup_keybindings ();
238
239         _tabs.set_show_border(false);
240         _tabs.signal_switch_page().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_switch));
241         _tabs.signal_page_added().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_added));
242         _tabs.signal_page_removed().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_removed));
243
244         rc_option_editor = new RCOptionEditor;
245         rc_option_editor->StateChange.connect (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_state_change));
246
247         if (create_editor ()) {
248                 error << _("UI: cannot setup editor") << endmsg;
249                 return -1;
250         }
251
252         if (create_mixer ()) {
253                 error << _("UI: cannot setup mixer") << endmsg;
254                 return -1;
255         }
256
257         if (create_meterbridge ()) {
258                 error << _("UI: cannot setup meterbridge") << endmsg;
259                 return -1;
260         }
261
262         if (create_luawindow ()) {
263                 error << _("UI: cannot setup luawindow") << endmsg;
264                 return -1;
265         }
266
267         /* order of addition affects order seen in initial window display */
268
269         rc_option_editor->add_to_notebook (_tabs, _("Preferences"));
270         mixer->add_to_notebook (_tabs, _("Mixer"));
271         editor->add_to_notebook (_tabs, _("Editor"));
272
273         time_info_box = new TimeInfoBox (false);
274         /* all other dialogs are created conditionally */
275
276         we_have_dependents ();
277
278 #ifdef TOP_MENUBAR
279         EventBox* status_bar_event_box = manage (new EventBox);
280
281         status_bar_event_box->add (status_bar_label);
282         status_bar_event_box->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
283         status_bar_label.set_size_request (300, -1);
284
285         status_bar_label.show ();
286         status_bar_event_box->show ();
287
288         status_bar_event_box->signal_button_press_event().connect (mem_fun (*this, &ARDOUR_UI::status_bar_button_press));
289
290         status_bar_hpacker.pack_start (*status_bar_event_box, true, true, 6);
291         status_bar_hpacker.pack_start (menu_bar_base, false, false, 2);
292 #else
293         top_packer.pack_start (menu_bar_base, false, false);
294 #endif
295
296         main_vpacker.pack_start (top_packer, false, false);
297
298         /* now add the transport frame to the top of main window */
299
300         main_vpacker.pack_start (transport_frame, false, false);
301         main_vpacker.pack_start (_tabs, true, true);
302
303 #ifdef TOP_MENUBAR
304         main_vpacker.pack_start (status_bar_hpacker, false, false);
305 #endif
306
307         LuaInstance::instance()->ActionChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::update_action_script_btn));
308
309         for (int i = 0; i < 9; ++i) {
310                 std::string const a = string_compose (X_("script-action-%1"), i + 1);
311                 Glib::RefPtr<Action> act = ActionManager::get_action(X_("Editor"), a.c_str());
312                 assert (act);
313                 action_script_call_btn[i].set_text (string_compose ("%1", i+1));
314                 action_script_call_btn[i].set_related_action (act);
315                 action_script_call_btn[i].signal_button_press_event().connect (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::bind_lua_action_script), i), false);
316                 if (act->get_sensitive ()) {
317                         action_script_call_btn[i].set_visual_state (Gtkmm2ext::VisualState (action_script_call_btn[i].visual_state() & ~Gtkmm2ext::Insensitive));
318                 } else {
319                         action_script_call_btn[i].set_visual_state (Gtkmm2ext::VisualState (action_script_call_btn[i].visual_state() | Gtkmm2ext::Insensitive));
320                 }
321                 const int row = i % 2;
322                 const int col = i / 2;
323                 action_script_table.attach (action_script_call_btn[i], col, col + 1, row, row + 1, EXPAND, EXPAND, 1, 0);
324                 action_script_call_btn[i].set_no_show_all ();
325         }
326         action_script_table.show ();
327
328         setup_transport();
329         build_menu_bar ();
330         setup_tooltips ();
331
332         _main_window.signal_delete_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::main_window_delete_event));
333
334         /* pack the main vpacker into the main window and show everything
335          */
336
337         _main_window.add (main_vpacker);
338         transport_frame.show_all ();
339
340         const XMLNode* mnode = main_window_settings ();
341
342         if (mnode) {
343                 XMLProperty const * prop;
344                 gint x = -1;
345                 gint y = -1;
346                 gint w = -1;
347                 gint h = -1;
348
349                 if ((prop = mnode->property (X_("x"))) != 0) {
350                         x = atoi (prop->value());
351                 }
352
353                 if ((prop = mnode->property (X_("y"))) != 0) {
354                         y = atoi (prop->value());
355                 }
356
357                 if ((prop = mnode->property (X_("w"))) != 0) {
358                         w = atoi (prop->value());
359                 }
360
361                 if ((prop = mnode->property (X_("h"))) != 0) {
362                         h = atoi (prop->value());
363                 }
364
365                 if (x >= 0 && y >= 0 && w >= 0 && h >= 0) {
366                         _main_window.set_position (Gtk::WIN_POS_NONE);
367                 }
368
369                 if (x >= 0 && y >= 0) {
370                         _main_window.move (x, y);
371                 }
372
373                 if (w > 0 && h > 0) {
374                         _main_window.set_default_size (w, h);
375                 }
376
377                 std::string current_tab;
378
379                 if ((prop = mnode->property (X_("current-tab"))) != 0) {
380                         current_tab = prop->value();
381                 } else {
382                         current_tab = "editor";
383                 }
384                 if (mixer && current_tab == "mixer") {
385                         _tabs.set_current_page (_tabs.page_num (mixer->contents()));
386                 } else if (rc_option_editor && current_tab == "preferences") {
387                         _tabs.set_current_page (_tabs.page_num (rc_option_editor->contents()));
388                 } else if (editor) {
389                         _tabs.set_current_page (_tabs.page_num (editor->contents()));
390                 }
391         }
392
393         setup_toplevel_window (_main_window, "", this);
394         _main_window.show_all ();
395
396         _tabs.set_show_tabs (false);
397
398         /* It would be nice if Gtkmm had wrapped this rather than just
399          * deprecating the old set_window_creation_hook() method, but oh well...
400          */
401         g_signal_connect (_tabs.gobj(), "create-window", (GCallback) ::tab_window_root_drop, this);
402
403         return 0;
404 }
405
406 bool
407 ARDOUR_UI::bind_lua_action_script (GdkEventButton*ev, int i)
408 {
409         if (ev->button != 3) {
410                 return false;
411         }
412         LuaInstance *li = LuaInstance::instance();
413         li->interactive_add (LuaScriptInfo::EditorAction, i);
414         return true;
415 }
416
417 void
418 ARDOUR_UI::update_action_script_btn (int i, const std::string&)
419 {
420         if (LuaInstance::instance()->lua_action_has_icon (i)) {
421                 uintptr_t ii = i;
422                 action_script_call_btn[i].set_icon (&LuaInstance::render_action_icon, (void*)ii);
423         } else {
424                 action_script_call_btn[i].set_icon (0, 0);
425         }
426 }