Merge branch 'master' of git@git.ardour.org:ardour/ardour
[ardour.git] / gtk2_ardour / window_manager.cc
1 /*
2     Copyright (C) 2013 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 #include <gtkmm/window.h>
20
21 #include "pbd/xml++.h"
22
23 #include "ardour/session_handle.h"
24
25 #include "gtkmm2ext/visibility_tracker.h"
26
27 #include "actions.h"
28 #include "ardour_dialog.h"
29 #include "ardour_window.h"
30 #include "window_manager.h"
31 #include "processor_box.h"
32
33 #include "i18n.h"
34
35 using std::string;
36 using namespace WM;
37 using namespace PBD;
38
39 Manager* Manager::_instance = 0;
40
41 Manager&
42 Manager::instance ()
43 {
44         if (!_instance) {
45                 _instance = new Manager;
46         }
47         return *_instance;
48 }
49
50 Manager::Manager ()
51         : current_transient_parent (0)
52 {
53 }
54
55 void
56 Manager::register_window (ProxyBase* info)
57 {
58         _windows.push_back (info);
59
60         if (!info->menu_name().empty()) {
61
62                 if (!window_actions) {
63                         window_actions = Gtk::ActionGroup::create (X_("Window"));
64                         ActionManager::add_action_group (window_actions);
65                 }
66
67                 info->set_action (ActionManager::register_action (window_actions, info->action_name().c_str(), info->menu_name().c_str(), 
68                                                                   sigc::bind (sigc::mem_fun (*this, &Manager::toggle_window), info)));
69         }
70 }
71
72 void
73 Manager::remove (const ProxyBase* info)
74 {
75         for (Windows::iterator i = _windows.begin(); i != _windows.end(); ++i) {
76                 if ((*i) == info) {
77                         _windows.erase (i);
78                         return;
79                 }
80         }
81 }
82
83 void
84 Manager::toggle_window (ProxyBase* proxy)
85 {
86         if (proxy) {
87                 proxy->toggle ();
88         }
89 }
90
91 void
92 Manager::show_visible() const
93 {
94         for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
95                 if ((*i)->visible()) {
96                         (*i)->show_all ();
97                         (*i)->present ();
98                 }
99         }
100 }
101
102 void
103 Manager::add_state (XMLNode& root) const
104 {
105         for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
106                 /* don't save state for temporary proxy windows
107                  */
108                 if (dynamic_cast<ProxyTemporary*> (*i)) {
109                         continue;
110                 }
111                 if (dynamic_cast<ProcessorWindowProxy*> (*i)) {
112                         ProcessorWindowProxy *pi = dynamic_cast<ProcessorWindowProxy*> (*i);
113                         root.add_child_nocopy (pi->get_state());
114                 } else {
115                         root.add_child_nocopy ((*i)->get_state());
116                 }
117         }
118 }
119
120 void
121 Manager::set_session (ARDOUR::Session* s)
122 {
123         for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
124                 ARDOUR::SessionHandlePtr* sp = (*i)->session_handle ();
125                 if (sp) {
126                         sp->set_session (s);
127                 }
128         }
129 }
130
131 void
132 Manager::set_transient_for (Gtk::Window* parent)
133 {
134         /* OS X has a richer concept of window layering than X does (or
135          * certainly, than any accepted conventions on X), and so the use of
136          * Manager::set_transient_for() is not necessary on that platform.
137          * 
138          * On OS X this is mostly taken care of by using the window type rather
139          * than explicit 1:1 transient-for relationships.
140          */
141
142 #ifndef __APPLE__
143         if (parent) {
144                 for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
145                         Gtk::Window* win = (*i)->get();
146                         if (win) {
147                                 win->set_transient_for (*parent);
148                         }
149                 }
150         } else {
151                 for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
152                         Gtk::Window* win = (*i)->get();
153                         if (win) {
154                                 gtk_window_set_transient_for (win->gobj(), 0);
155                         }
156                 }
157         }
158         
159         current_transient_parent = parent;
160 #endif
161 }
162
163 /*-------------------------*/
164
165 ProxyBase::ProxyBase (const string& name, const std::string& menu_name)
166         : _name (name)
167         , _menu_name (menu_name)
168         , _window (0)
169         , _visible (false)
170         , _x_off (-1)
171         , _y_off (-1)
172         , _width (-1)
173         , _height (-1) 
174         , vistracker (0)
175 {
176 }
177
178 ProxyBase::ProxyBase (const string& name, const std::string& menu_name, const XMLNode& node)
179         : _name (name)
180         , _menu_name (menu_name)
181         , _window (0)
182         , _visible (false)
183         , _x_off (-1)
184         , _y_off (-1)
185         , _width (-1)
186         , _height (-1) 
187         , vistracker (0)
188 {
189         set_state (node);
190 }
191
192 ProxyBase::~ProxyBase ()
193 {
194         delete vistracker;
195 }
196
197 void
198 ProxyBase::set_state (const XMLNode& node)
199 {
200         XMLNodeList children = node.children ();
201
202         XMLNodeList::const_iterator i = children.begin ();
203
204         while (i != children.end()) {
205                 XMLProperty* prop = (*i)->property (X_("name"));
206                 if ((*i)->name() == X_("Window") && prop && prop->value() == _name) {
207                         break;
208                 }
209
210                 ++i;
211         }
212
213         if (i != children.end()) {
214
215                 XMLProperty* prop;
216
217                 if ((prop = (*i)->property (X_("visible"))) != 0) {
218                         _visible = PBD::string_is_affirmative (prop->value ());
219                 }
220
221                 if ((prop = (*i)->property (X_("x-off"))) != 0) {
222                         _x_off = atoi (prop->value());
223                 }
224                 if ((prop = (*i)->property (X_("y-off"))) != 0) {
225                         _y_off = atoi (prop->value());
226                 }
227                 if ((prop = (*i)->property (X_("x-size"))) != 0) {
228                         _width = atoi (prop->value());
229                 }
230                 if ((prop = (*i)->property (X_("y-size"))) != 0) {
231                         _height = atoi (prop->value());
232                 }
233         }
234
235         /* if we have a window already, reset its properties */
236
237         if (_window) {
238                 setup ();
239         }
240 }
241
242 void
243 ProxyBase::set_action (Glib::RefPtr<Gtk::Action> act)
244 {
245         _action = act;
246 }
247
248 std::string
249 ProxyBase::action_name() const 
250 {
251         return string_compose (X_("toggle-%1"), _name);
252 }
253
254 void
255 ProxyBase::toggle() 
256 {
257         if (!_window) {
258                 (void) get (true);
259                 assert (_window);
260                 /* XXX this is a hack - the window object should really
261                    ensure its components are all visible. sigh.
262                 */
263                 _window->show_all();
264                 /* we'd like to just call this and nothing else */
265                 _window->present ();
266         } else {
267                 vistracker->cycle_visibility ();
268         }
269 }
270
271 XMLNode&
272 ProxyBase::get_state () const
273 {
274         XMLNode* node = new XMLNode (X_("Window"));
275         char buf[32];   
276
277         node->add_property (X_("name"), _name);
278
279         if (_window && vistracker) {
280                 
281                 /* we have a window, so use current state */
282
283                 _visible = vistracker->partially_visible ();
284                 _window->get_position (_x_off, _y_off);
285                 _window->get_size (_width, _height);
286         }
287
288         node->add_property (X_("visible"), _visible? X_("yes") : X_("no"));
289         
290         snprintf (buf, sizeof (buf), "%d", _x_off);
291         node->add_property (X_("x-off"), buf);
292         snprintf (buf, sizeof (buf), "%d", _y_off);
293         node->add_property (X_("y-off"), buf);
294         snprintf (buf, sizeof (buf), "%d", _width);
295         node->add_property (X_("x-size"), buf);
296         snprintf (buf, sizeof (buf), "%d", _height);
297         node->add_property (X_("y-size"), buf);
298
299         return *node;
300 }
301
302 void
303 ProxyBase::drop_window ()
304 {
305         if (_window) {
306                 _window->hide ();
307                 delete _window;
308                 _window = 0;
309                 delete vistracker;
310                 vistracker = 0;
311         }
312 }
313
314 void
315 ProxyBase::use_window (Gtk::Window& win)
316 {
317         drop_window ();
318         _window = &win;
319         setup ();
320 }
321
322 void
323 ProxyBase::setup ()
324 {
325         assert (_window);
326
327         vistracker = new Gtkmm2ext::VisibilityTracker (*_window);
328
329         if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
330                 /* cancel any mouse-based positioning */
331                 _window->set_position (Gtk::WIN_POS_NONE);
332         }
333
334         if (_width != -1 && _height != -1) {
335                 _window->set_default_size (_width, _height);
336         }
337
338         if (_x_off != -1 && _y_off != -1) {
339                 _window->move (_x_off, _y_off);
340         }
341 }
342         
343 void
344 ProxyBase::show ()
345 {
346         Gtk::Window* win = get (true);
347         win->show ();
348 }
349
350 void
351 ProxyBase::maybe_show ()
352 {
353         if (_visible) {
354                 show ();
355         }
356 }
357
358 void
359 ProxyBase::show_all ()
360 {
361         Gtk::Window* win = get (true);
362         win->show_all ();
363 }
364
365
366 void
367 ProxyBase::present ()
368 {
369         Gtk::Window* win = get (true);
370         win->show_all ();
371         win->present ();
372
373         /* turn off any mouse-based positioning */
374         _window->set_position (Gtk::WIN_POS_NONE);
375 }
376
377 void
378 ProxyBase::hide ()
379 {
380         Gtk::Window* win = get (false);
381         if (win) {
382                 win->hide ();
383         }
384 }
385
386 /*-----------------------*/
387
388 ProxyTemporary::ProxyTemporary (const string& name, Gtk::Window* win)
389         : ProxyBase (name, string())
390 {
391         _window = win;
392 }
393
394 ProxyTemporary::~ProxyTemporary ()
395 {
396 }
397
398 ARDOUR::SessionHandlePtr*
399 ProxyTemporary::session_handle()
400 {
401         /* may return null */
402         ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
403         if (aw) { return aw; }
404         ArdourDialog* ad = dynamic_cast<ArdourDialog*> (_window);
405         if (ad) { return ad; }
406         return 0;
407 }