a bunch of stuff to make tab/window switching work better, and provide Alt-m to toggl...
[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 "mixer_ui.h"
41 #include "keyboard.h"
42 #include "splash.h"
43 #include "rc_option_editor.h"
44 #include "route_params_ui.h"
45 #include "opts.h"
46 #include "utils.h"
47
48 #include "i18n.h"
49
50 using namespace Gtk;
51 using namespace PBD;
52
53 namespace ARDOUR {
54         class Session;
55         class Route;
56 }
57
58 using namespace ARDOUR;
59
60 void
61 ARDOUR_UI::we_have_dependents ()
62 {
63         install_actions ();
64         load_bindings ();
65
66         ProcessorBox::register_actions ();
67
68         /* Global, editor, mixer, processor box actions are defined now. Link
69            them with any bindings, so that GTK does not get a chance to define
70            the GTK accel map entries first when we ask the GtkUIManager to
71            create menus/widgets.
72
73            If GTK adds the actions to its accel map before we do, we lose our
74            freedom to use any keys. More precisely, we can use any keys, but
75            ones that GTK considers illegal as accelerators will not show up in
76            menus.
77
78            There are other dynamic actions that can be created by a monitor
79            section, by step entry dialogs. These need to be handled
80            separately. They don't tend to use GTK-illegal bindings and more
81            importantly they don't have menus showing the bindings, so it is
82            less of an issue.
83         */
84
85         Gtkmm2ext::Bindings::associate_all ();
86
87         editor->setup_tooltips ();
88         editor->UpdateAllTransportClocks.connect (sigc::mem_fun (*this, &ARDOUR_UI::update_transport_clocks));
89
90         /* catch up on tabbable state, in the right order to leave the editor
91          * selected by default
92          */
93
94         tabbable_state_change (*rc_option_editor);
95         tabbable_state_change (*mixer);
96         tabbable_state_change (*editor);
97
98         /* all actions are defined */
99
100         ActionManager::load_menus (ARDOUR_COMMAND_LINE::menus_file);
101
102         editor->track_mixer_selection ();
103         mixer->track_editor_selection ();
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
123         /* its safe to do this now */
124
125         BootMessage (_("Reload Session History"));
126         s->restore_history ("");
127 }
128
129 /** The main editor window has been closed */
130 gint
131 ARDOUR_UI::exit_on_main_window_close (GdkEventAny * /*ev*/)
132 {
133 #ifdef TOP_MENUBAR
134         /* just hide the window, and return - the top menu stays up */
135         editor->hide ();
136         return TRUE;
137 #else
138         /* time to get out of here */
139         finish();
140         return TRUE;
141 #endif
142 }
143
144 GtkNotebook*
145 ARDOUR_UI::tab_window_root_drop (GtkNotebook* src,
146                                  GtkWidget* w,
147                                  gint x,
148                                  gint y,
149                                  gpointer)
150 {
151         using namespace std;
152         Gtk::Notebook* nb = 0;
153         Gtk::Window* win = 0;
154         Gtkmm2ext::Tabbable* tabbable = 0;
155
156
157         if (w == GTK_WIDGET(editor->contents().gobj())) {
158                 tabbable = editor;
159         } else if (w == GTK_WIDGET(mixer->contents().gobj())) {
160                 tabbable = mixer;
161         } else if (w == GTK_WIDGET(rc_option_editor->contents().gobj())) {
162                 tabbable = rc_option_editor;
163         } else {
164                 return 0;
165         }
166
167         nb = tabbable->tab_root_drop ();
168         win = tabbable->own_window ();
169
170         if (nb) {
171                 win->move (x, y);
172                 win->show_all ();
173                 win->present ();
174                 return nb->gobj();
175         }
176
177         return 0; /* what was that? */
178 }
179
180 bool
181 ARDOUR_UI::idle_ask_about_quit ()
182 {
183         if (_session && _session->dirty()) {
184                 finish ();
185         } else {
186                 /* no session or session not dirty, but still ask anyway */
187
188                 Gtk::MessageDialog msg (string_compose ("Quit %1?", PROGRAM_NAME),
189                                         false, /* no markup */
190                                         Gtk::MESSAGE_INFO,
191                                         Gtk::BUTTONS_YES_NO,
192                                         true); /* modal */
193                 msg.set_default_response (Gtk::RESPONSE_YES);
194
195                 if (msg.run() == Gtk::RESPONSE_YES) {
196                         finish ();
197                 }
198         }
199
200         /* not reached but keep the compiler happy */
201
202         return false;
203 }
204
205 bool
206 ARDOUR_UI::main_window_delete_event (GdkEventAny* ev)
207 {
208         /* quit the application as soon as we go idle. If we call this here,
209          * the window manager/desktop can think we're taking too longer to
210          * handle the "delete" event
211          */
212
213         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::idle_ask_about_quit));
214
215         return true;
216 }
217
218 static GtkNotebook*
219 tab_window_root_drop (GtkNotebook* src,
220                       GtkWidget* w,
221                       gint x,
222                       gint y,
223                       gpointer user_data)
224 {
225         return ARDOUR_UI::instance()->tab_window_root_drop (src, w, x, y, user_data);
226 }
227
228 int
229 ARDOUR_UI::setup_windows ()
230 {
231         /* actions do not need to be defined when we load keybindings. They
232          * will be lazily discovered. But bindings do need to exist when we
233          * create windows/tabs with their own binding sets.
234          */
235
236         keyboard->setup_keybindings ();
237
238         _tabs.signal_switch_page().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_switch));
239         _tabs.signal_page_added().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_added));
240         _tabs.signal_page_removed().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_removed));
241
242         rc_option_editor = new RCOptionEditor;
243         rc_option_editor->StateChange.connect (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_state_change));
244
245         if (create_editor ()) {
246                 error << _("UI: cannot setup editor") << endmsg;
247                 return -1;
248         }
249
250         if (create_mixer ()) {
251                 error << _("UI: cannot setup mixer") << endmsg;
252                 return -1;
253         }
254
255         if (create_meterbridge ()) {
256                 error << _("UI: cannot setup meterbridge") << endmsg;
257                 return -1;
258         }
259
260         /* order of addition affects order seen in initial window display */
261
262         rc_option_editor->add_to_notebook (_tabs, _("Preferences"));
263         mixer->add_to_notebook (_tabs, _("Mixer"));
264         editor->add_to_notebook (_tabs, _("Editor"));
265
266         /* all other dialogs are created conditionally */
267
268         we_have_dependents ();
269
270 #ifdef TOP_MENUBAR
271         EventBox* status_bar_event_box = manage (new EventBox);
272
273         status_bar_event_box->add (status_bar_label);
274         status_bar_event_box->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
275         status_bar_label.set_size_request (300, -1);
276
277         status_bar_label.show ();
278         status_bar_event_box->show ();
279
280         status_bar_event_box->signal_button_press_event().connect (mem_fun (*this, &ARDOUR_UI::status_bar_button_press));
281
282         status_bar_hpacker.pack_start (*status_bar_event_box, true, true, 6);
283         status_bar_hpacker.pack_start (menu_bar_base, false, false, 2);
284 #else
285         top_packer.pack_start (menu_bar_base, false, false);
286 #endif
287
288         main_vpacker.pack_start (top_packer, false, false);
289
290         /* now add the transport frame to the top of main window */
291
292         main_vpacker.pack_start (transport_frame, false, false);
293         main_vpacker.pack_start (_tabs, true, true);
294
295 #ifdef TOP_MENUBAR
296         main_vpacker.pack_start (status_bar_hpacker, false, false);
297 #endif
298
299         setup_transport();
300         build_menu_bar ();
301         setup_tooltips ();
302
303         _main_window.signal_delete_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::main_window_delete_event));
304
305         /* pack the main vpacker into the main window and show everything
306          */
307
308         _main_window.add (main_vpacker);
309         transport_frame.show_all ();
310
311         const XMLNode* mnode = main_window_settings ();
312
313         if (mnode) {
314                 const XMLProperty* prop;
315                 gint x = -1;
316                 gint y = -1;
317                 gint w = -1;
318                 gint h = -1;
319
320                 if ((prop = mnode->property (X_("x"))) != 0) {
321                         x = atoi (prop->value());
322                 }
323
324                 if ((prop = mnode->property (X_("y"))) != 0) {
325                         y = atoi (prop->value());
326                 }
327
328                 if ((prop = mnode->property (X_("w"))) != 0) {
329                         w = atoi (prop->value());
330                 }
331
332                 if ((prop = mnode->property (X_("h"))) != 0) {
333                         h = atoi (prop->value());
334                 }
335
336                 if (x >= 0 && y >= 0 && w >= 0 && h >= 0) {
337                         _main_window.set_position (Gtk::WIN_POS_NONE);
338                 }
339
340                 if (x >= 0 && y >= 0) {
341                         _main_window.move (x, y);
342                 }
343
344                 if (w > 0 && h > 0) {
345                         _main_window.set_default_size (w, h);
346                 }
347
348                 std::string current_tab;
349
350                 if ((prop = mnode->property (X_("current-tab"))) != 0) {
351                         current_tab = prop->value();
352                 } else {
353                         current_tab = "editor";
354                 }
355                 if (mixer && current_tab == "mixer") {
356                         _tabs.set_current_page (_tabs.page_num (mixer->contents()));
357                 } else if (rc_option_editor && current_tab == "preferences") {
358                         _tabs.set_current_page (_tabs.page_num (rc_option_editor->contents()));
359                 } else if (editor) {
360                         _tabs.set_current_page (_tabs.page_num (editor->contents()));
361                 }
362         }
363
364         setup_toplevel_window (_main_window, "", this);
365         _main_window.show_all ();
366
367         _tabs.set_show_tabs (false);
368
369         /* It would be nice if Gtkmm had wrapped this rather than just
370          * deprecating the old set_window_creation_hook() method, but oh well...
371          */
372         g_signal_connect (_tabs.gobj(), "create-window", (GCallback) ::tab_window_root_drop, this);
373
374         return 0;
375 }