Merge branch 'master' into cairocanvas
[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         /* OS X has a richer concept of window layering than X does (or
134          * certainly, than any accepted conventions on X), and so the use of
135          * Manager::set_transient_for() is not necessary on that platform.
136          * 
137          * On OS X this is mostly taken care of by using the window type rather
138          * than explicit 1:1 transient-for relationships.
139          */
140
141 #ifndef __APPLE__
142         if (parent) {
143                 for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
144                         Gtk::Window* win = (*i)->get();
145                         if (win) {
146                                 win->set_transient_for (*parent);
147                         }
148                 }
149         } else {
150                 for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
151                         Gtk::Window* win = (*i)->get();
152                         if (win) {
153                                 gtk_window_set_transient_for (win->gobj(), 0);
154                         }
155                 }
156         }
157         
158         current_transient_parent = parent;
159 #endif
160 }
161
162 /*-------------------------*/
163
164 ProxyBase::ProxyBase (const string& name, const std::string& menu_name)
165         : _name (name)
166         , _menu_name (menu_name)
167         , _window (0)
168         , _visible (false)
169         , _x_off (-1)
170         , _y_off (-1)
171         , _width (-1)
172         , _height (-1) 
173         , vistracker (0)
174 {
175 }
176
177 ProxyBase::ProxyBase (const string& name, const std::string& menu_name, const XMLNode& node)
178         : _name (name)
179         , _menu_name (menu_name)
180         , _window (0)
181         , _visible (false)
182         , _x_off (-1)
183         , _y_off (-1)
184         , _width (-1)
185         , _height (-1) 
186         , vistracker (0)
187 {
188         set_state (node);
189 }
190
191 ProxyBase::~ProxyBase ()
192 {
193         delete vistracker;
194 }
195
196 void
197 ProxyBase::set_state (const XMLNode& node)
198 {
199         XMLNodeList children = node.children ();
200
201         XMLNodeList::const_iterator i = children.begin ();
202
203         while (i != children.end()) {
204                 XMLProperty* prop = (*i)->property (X_("name"));
205                 if ((*i)->name() == X_("Window") && prop && prop->value() == _name) {
206                         break;
207                 }
208
209                 ++i;
210         }
211
212         if (i != children.end()) {
213
214                 XMLProperty* prop;
215
216                 if ((prop = (*i)->property (X_("visible"))) != 0) {
217                         _visible = PBD::string_is_affirmative (prop->value ());
218                 }
219
220                 if ((prop = (*i)->property (X_("x-off"))) != 0) {
221                         _x_off = atoi (prop->value().c_str());
222                 }
223                 if ((prop = (*i)->property (X_("y-off"))) != 0) {
224                         _y_off = atoi (prop->value().c_str());
225                 }
226                 if ((prop = (*i)->property (X_("x-size"))) != 0) {
227                         _width = atoi (prop->value().c_str());
228                 }
229                 if ((prop = (*i)->property (X_("y-size"))) != 0) {
230                         _height = atoi (prop->value().c_str());
231                 }
232         }
233
234         /* if we have a window already, reset its properties */
235
236         if (_window) {
237                 setup ();
238         }
239 }
240
241 void
242 ProxyBase::set_action (Glib::RefPtr<Gtk::Action> act)
243 {
244         _action = act;
245 }
246
247 std::string
248 ProxyBase::action_name() const 
249 {
250         return string_compose (X_("toggle-%1"), _name);
251 }
252
253 void
254 ProxyBase::toggle() 
255 {
256         if (!_window) {
257                 (void) get (true);
258                 assert (_window);
259                 /* XXX this is a hack - the window object should really
260                    ensure its components are all visible. sigh.
261                 */
262                 _window->show_all();
263                 /* we'd like to just call this and nothing else */
264                 _window->present ();
265         } else {
266                 vistracker->cycle_visibility ();
267         }
268 }
269
270 XMLNode&
271 ProxyBase::get_state () const
272 {
273         XMLNode* node = new XMLNode (X_("Window"));
274         char buf[32];   
275
276         node->add_property (X_("name"), _name);
277
278         if (_window && vistracker) {
279                 
280                 /* we have a window, so use current state */
281
282                 _visible = vistracker->partially_visible ();
283                 _window->get_position (_x_off, _y_off);
284                 _window->get_size (_width, _height);
285         }
286
287         node->add_property (X_("visible"), _visible? X_("yes") : X_("no"));
288         
289         snprintf (buf, sizeof (buf), "%d", _x_off);
290         node->add_property (X_("x-off"), buf);
291         snprintf (buf, sizeof (buf), "%d", _y_off);
292         node->add_property (X_("y-off"), buf);
293         snprintf (buf, sizeof (buf), "%d", _width);
294         node->add_property (X_("x-size"), buf);
295         snprintf (buf, sizeof (buf), "%d", _height);
296         node->add_property (X_("y-size"), buf);
297
298         return *node;
299 }
300
301 void
302 ProxyBase::drop_window ()
303 {
304         if (_window) {
305                 _window->hide ();
306                 delete _window;
307                 _window = 0;
308                 delete vistracker;
309                 vistracker = 0;
310         }
311 }
312
313 void
314 ProxyBase::use_window (Gtk::Window& win)
315 {
316         drop_window ();
317         _window = &win;
318         setup ();
319 }
320
321 void
322 ProxyBase::setup ()
323 {
324         assert (_window);
325
326         vistracker = new Gtkmm2ext::VisibilityTracker (*_window);
327
328         if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
329                 /* cancel any mouse-based positioning */
330                 _window->set_position (Gtk::WIN_POS_NONE);
331         }
332
333         if (_width != -1 && _height != -1) {
334                 _window->set_default_size (_width, _height);
335         }
336
337         if (_x_off != -1 && _y_off != -1) {
338                 _window->move (_x_off, _y_off);
339         }
340 }
341         
342 void
343 ProxyBase::show ()
344 {
345         Gtk::Window* win = get (true);
346         win->show ();
347 }
348
349 void
350 ProxyBase::maybe_show ()
351 {
352         if (_visible) {
353                 show ();
354         }
355 }
356
357 void
358 ProxyBase::show_all ()
359 {
360         Gtk::Window* win = get (true);
361         win->show_all ();
362 }
363
364
365 void
366 ProxyBase::present ()
367 {
368         Gtk::Window* win = get (true);
369         win->show_all ();
370         win->present ();
371
372         /* turn off any mouse-based positioning */
373         _window->set_position (Gtk::WIN_POS_NONE);
374 }
375
376 void
377 ProxyBase::hide ()
378 {
379         Gtk::Window* win = get (false);
380         if (win) {
381                 win->hide ();
382         }
383 }
384
385 /*-----------------------*/
386
387 ProxyTemporary::ProxyTemporary (const string& name, Gtk::Window* win)
388         : ProxyBase (name, string())
389 {
390         _window = win;
391 }
392
393 ProxyTemporary::~ProxyTemporary ()
394 {
395 }
396
397 ARDOUR::SessionHandlePtr*
398 ProxyTemporary::session_handle()
399 {
400         /* may return null */
401         ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
402         if (aw) { return aw; }
403         ArdourDialog* ad = dynamic_cast<ArdourDialog*> (_window);
404         if (ad) { return ad; }
405         return 0;
406 }