tab-free tabbed display, part 1.2
[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         _tabs.signal_switch_page().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_switch));
246
247         rc_option_editor = new RCOptionEditor;
248         rc_option_editor->StateChange.connect (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_state_change));
249
250         if (create_editor ()) {
251                 error << _("UI: cannot setup editor") << endmsg;
252                 return -1;
253         }
254
255         if (create_mixer ()) {
256                 error << _("UI: cannot setup mixer") << endmsg;
257                 return -1;
258         }
259
260         if (create_meterbridge ()) {
261                 error << _("UI: cannot setup meterbridge") << endmsg;
262                 return -1;
263         }
264
265         /* order of addition affects order seen in initial window display */
266
267         rc_option_editor->add_to_notebook (_tabs, _("Preferences"));
268         mixer->add_to_notebook (_tabs, _("Mixer"));
269         editor->add_to_notebook (_tabs, _("Editor"));
270
271         /* all other dialogs are created conditionally */
272
273         we_have_dependents ();
274
275 #ifdef TOP_MENUBAR
276         EventBox* status_bar_event_box = manage (new EventBox);
277
278         status_bar_event_box->add (status_bar_label);
279         status_bar_event_box->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
280         status_bar_label.set_size_request (300, -1);
281
282         status_bar_label.show ();
283         status_bar_event_box->show ();
284
285         status_bar_event_box->signal_button_press_event().connect (mem_fun (*this, &ARDOUR_UI::status_bar_button_press));
286
287         status_bar_hpacker.pack_start (*status_bar_event_box, true, true, 6);
288         status_bar_hpacker.pack_start (menu_bar_base, false, false, 2);
289 #else
290         top_packer.pack_start (menu_bar_base, false, false);
291 #endif
292
293         main_vpacker.pack_start (top_packer, false, false);
294
295         /* now add the transport frame to the top of main window */
296
297         main_vpacker.pack_start (transport_frame, false, false);
298         main_vpacker.pack_start (_tabs, true, true);
299
300 #ifdef TOP_MENUBAR
301         main_vpacker.pack_start (status_bar_hpacker, false, false);
302 #endif
303
304         setup_transport();
305         build_menu_bar ();
306         setup_tooltips ();
307
308         _main_window.signal_delete_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::main_window_delete_event));
309
310         /* pack the main vpacker into the main window and show everything
311          */
312
313         _main_window.add (main_vpacker);
314         transport_frame.show_all ();
315
316         const XMLNode* mnode = main_window_settings ();
317
318         if (mnode) {
319                 const XMLProperty* prop;
320                 gint x = -1;
321                 gint y = -1;
322                 gint w = -1;
323                 gint h = -1;
324
325                 if ((prop = mnode->property (X_("x"))) != 0) {
326                         x = atoi (prop->value());
327                 }
328
329                 if ((prop = mnode->property (X_("y"))) != 0) {
330                         y = atoi (prop->value());
331                 }
332
333                 if ((prop = mnode->property (X_("w"))) != 0) {
334                         w = atoi (prop->value());
335                 }
336
337                 if ((prop = mnode->property (X_("h"))) != 0) {
338                         h = atoi (prop->value());
339                 }
340
341                 if (x >= 0 && y >= 0 && w >= 0 && h >= 0) {
342                         _main_window.set_position (Gtk::WIN_POS_NONE);
343                 }
344
345                 if (x >= 0 && y >= 0) {
346                         _main_window.move (x, y);
347                 }
348
349                 if (w > 0 && h > 0) {
350                         _main_window.set_default_size (w, h);
351                 }
352
353                 std::string current_tab;
354
355                 if ((prop = mnode->property (X_("current-tab"))) != 0) {
356                         current_tab = prop->value();
357                 } else {
358                         current_tab = "editor";
359                 }
360                 if (mixer && current_tab == "mixer") {
361                         _tabs.set_current_page (_tabs.page_num (mixer->contents()));
362                 } else if (rc_option_editor && current_tab == "preferences") {
363                         _tabs.set_current_page (_tabs.page_num (rc_option_editor->contents()));
364                 } else if (editor) {
365                         _tabs.set_current_page (_tabs.page_num (editor->contents()));
366                 }
367         }
368
369         _main_window.show_all ();
370         setup_toplevel_window (_main_window, "", this);
371
372         _tabs.set_show_tabs (false);
373
374         /* It would be nice if Gtkmm had wrapped this rather than just
375          * deprecating the old set_window_creation_hook() method, but oh well...
376          */
377         g_signal_connect (_tabs.gobj(), "create-window", (GCallback) ::tab_window_root_drop, this);
378
379         return 0;
380 }