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