change suffix of keybinding files to ".keys" to avoid conflict with earlier versions.
[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 */
91
92         tabbable_state_change (*editor);
93         tabbable_state_change (*mixer);
94         tabbable_state_change (*rc_option_editor);
95         
96         /* all actions are defined */
97
98         ActionManager::load_menus (ARDOUR_COMMAND_LINE::menus_file);
99
100         editor->track_mixer_selection ();
101         mixer->track_editor_selection ();
102
103         /* catch up on parameters */
104         
105         boost::function<void (std::string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
106         Config->map_parameters (pc);
107
108         UIConfiguration::instance().reset_dpi ();
109 }
110
111 void
112 ARDOUR_UI::connect_dependents_to_session (ARDOUR::Session *s)
113 {
114         DisplaySuspender ds;
115         BootMessage (_("Setup Editor"));
116         editor->set_session (s);
117         BootMessage (_("Setup Mixer"));
118         mixer->set_session (s);
119         meterbridge->set_session (s);
120
121         /* its safe to do this now */
122
123         BootMessage (_("Reload Session History"));
124         s->restore_history ("");
125 }
126
127 /** The main editor window has been closed */
128 gint
129 ARDOUR_UI::exit_on_main_window_close (GdkEventAny * /*ev*/)
130 {
131 #ifdef TOP_MENUBAR
132         /* just hide the window, and return - the top menu stays up */
133         editor->hide ();
134         return TRUE;
135 #else
136         /* time to get out of here */
137         finish();
138         return TRUE;
139 #endif
140 }
141
142 GtkNotebook*
143 ARDOUR_UI::tab_window_root_drop (GtkNotebook* src,
144                                  GtkWidget* w,
145                                  gint x,
146                                  gint y,
147                                  gpointer)
148 {
149         using namespace std;
150         Gtk::Notebook* nb = 0;
151         Gtk::Window* win = 0;
152         Gtkmm2ext::Tabbable* tabbable = 0;
153
154
155         if (w == GTK_WIDGET(editor->contents().gobj())) {
156                 tabbable = editor;
157         } else if (w == GTK_WIDGET(mixer->contents().gobj())) {
158                 tabbable = mixer;
159         } else if (w == GTK_WIDGET(rc_option_editor->contents().gobj())) {
160                 tabbable = rc_option_editor;
161         } else {
162                 return 0;
163         }
164
165         nb = tabbable->tab_root_drop ();
166         win = tabbable->own_window ();
167
168         if (nb) {
169                 win->move (x, y);
170                 win->show_all ();
171                 win->present ();
172                 return nb->gobj();
173         }
174         
175         return 0; /* what was that? */
176 }
177
178 bool
179 ARDOUR_UI::idle_ask_about_quit ()
180 {
181         if (_session && _session->dirty()) {
182                 finish ();
183         } else {
184                 /* no session or session not dirty, but still ask anyway */
185
186                 Gtk::MessageDialog msg (string_compose ("Quit %1?", PROGRAM_NAME),
187                                         false, /* no markup */
188                                         Gtk::MESSAGE_INFO,
189                                         Gtk::BUTTONS_YES_NO,
190                                         true); /* modal */
191                 msg.set_default_response (Gtk::RESPONSE_YES);
192
193                 if (msg.run() == Gtk::RESPONSE_YES) {
194                         finish ();
195                 }
196         }
197
198         /* not reached but keep the compiler happy */
199
200         return false;
201 }
202
203 bool
204 ARDOUR_UI::main_window_delete_event (GdkEventAny* ev)
205 {
206         /* quit the application as soon as we go idle. If we call this here,
207          * the window manager/desktop can think we're taking too longer to
208          * handle the "delete" event
209          */
210         
211         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::idle_ask_about_quit));   
212         
213         return true;
214 }
215
216 static GtkNotebook*
217 tab_window_root_drop (GtkNotebook* src,
218                       GtkWidget* w,
219                       gint x,
220                       gint y,
221                       gpointer user_data)
222 {
223         return ARDOUR_UI::instance()->tab_window_root_drop (src, w, x, y, user_data);
224 }
225
226 int
227 ARDOUR_UI::setup_windows ()
228 {
229         /* actions do not need to be defined when we load keybindings. They
230          * will be lazily discovered. But bindings do need to exist when we
231          * create windows/tabs with their own binding sets.
232          */
233
234         keyboard->setup_keybindings ();
235
236         /* we don't use a widget with its own window for the tab close button,
237            which makes it impossible to rely on GTK+ to generate signals for
238            events occuring "in" this widget. Instead, we pre-connect a
239            handler to the relevant events on the notebook and then check
240            to see if the event coordinates tell us that it occured "in"
241            the close button.
242         */
243         _tabs.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_button_event), false);
244         _tabs.signal_button_release_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_button_event), false);
245
246         rc_option_editor = new RCOptionEditor;
247         rc_option_editor->StateChange.connect (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_state_change));
248
249         if (create_editor ()) {
250                 error << _("UI: cannot setup editor") << endmsg;
251                 return -1;
252         }
253
254         if (create_mixer ()) {
255                 error << _("UI: cannot setup mixer") << endmsg;
256                 return -1;
257         }
258
259         if (create_meterbridge ()) {
260                 error << _("UI: cannot setup meterbridge") << endmsg;
261                 return -1;
262         }
263
264         /* order of addition affects order seen in initial window display */
265         
266         rc_option_editor->add_to_notebook (_tabs, _("Preferences"));
267         mixer->add_to_notebook (_tabs, _("Mixer"));
268         editor->add_to_notebook (_tabs, _("Editor"));
269
270         /* all other dialogs are created conditionally */
271
272         we_have_dependents ();
273
274 #ifdef TOP_MENUBAR
275         EventBox* status_bar_event_box = manage (new EventBox);
276
277         status_bar_event_box->add (status_bar_label);
278         status_bar_event_box->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
279         status_bar_label.set_size_request (300, -1);
280
281         status_bar_label.show ();
282         status_bar_event_box->show ();
283
284         status_bar_event_box->signal_button_press_event().connect (mem_fun (*this, &ARDOUR_UI::status_bar_button_press));
285
286         status_bar_hpacker.pack_start (*status_bar_event_box, true, true, 6);
287         status_bar_hpacker.pack_start (menu_bar_base, false, false, 2);
288 #else
289         top_packer.pack_start (menu_bar_base, false, false);
290 #endif
291
292         main_vpacker.pack_start (top_packer, false, false);
293
294         /* now add the transport frame to the top of main window */
295         
296         main_vpacker.pack_start (transport_frame, false, false);
297         main_vpacker.pack_start (_tabs, true, true);
298
299 #ifdef TOP_MENUBAR
300         main_vpacker.pack_start (status_bar_hpacker, false, false);
301 #endif
302
303         setup_transport();
304         build_menu_bar ();
305         setup_tooltips ();
306
307         _main_window.signal_delete_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::main_window_delete_event));
308         
309         /* pack the main vpacker into the main window and show everything
310          */
311
312         _main_window.add (main_vpacker);
313         transport_frame.show_all ();
314
315         const XMLNode* mnode = main_window_settings ();
316
317         if (mnode) {
318                 const XMLProperty* prop;
319                 gint x = -1;
320                 gint y = -1;
321                 gint w = -1;
322                 gint h = -1;
323
324                 if ((prop = mnode->property (X_("x"))) != 0) {
325                         x = atoi (prop->value());
326                 }
327
328                 if ((prop = mnode->property (X_("y"))) != 0) {
329                         y = atoi (prop->value());
330                 } 
331
332                 if ((prop = mnode->property (X_("w"))) != 0) {
333                         w = atoi (prop->value());
334                 } 
335                 
336                 if ((prop = mnode->property (X_("h"))) != 0) {
337                         h = atoi (prop->value());
338                 }
339
340                 if (x >= 0 && y >= 0 && w >= 0 && h >= 0) {
341                         _main_window.set_position (Gtk::WIN_POS_NONE);
342                 }
343                 
344                 if (x >= 0 && y >= 0) {
345                         _main_window.move (x, y);
346                 }
347                 
348                 if (w > 0 && h > 0) {
349                         _main_window.set_default_size (w, h);
350                 }
351
352                 std::string current_tab;
353                 
354                 if ((prop = mnode->property (X_("current-tab"))) != 0) {
355                         current_tab = prop->value();
356                 } else {
357                         current_tab = "editor";
358                 }
359                 if (mixer && current_tab == "mixer") {
360                         _tabs.set_current_page (_tabs.page_num (mixer->contents()));
361                 } else if (rc_option_editor && current_tab == "preferences") {
362                         _tabs.set_current_page (_tabs.page_num (rc_option_editor->contents()));
363                 } else if (editor) {
364                         _tabs.set_current_page (_tabs.page_num (editor->contents()));
365                 }
366         }
367         
368         _main_window.show_all ();
369         setup_toplevel_window (_main_window, "", this);
370         
371         _tabs.signal_switch_page().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_switch));
372         _tabs.signal_page_removed().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_removed));
373         _tabs.signal_page_added().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_page_added));
374
375         /* It would be nice if Gtkmm had wrapped this rather than just
376          * deprecating the old set_window_creation_hook() method, but oh well...
377          */
378         g_signal_connect (_tabs.gobj(), "create-window", (GCallback) ::tab_window_root_drop, this);
379
380         return 0;
381 }