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