1 /*
2  * Copyright (C) 2013-2018 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2013-2018 Robin Gareus <robin@gareus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <gtkmm/window.h>
21 
22 #include "pbd/xml++.h"
23 
24 #include "ardour/session_handle.h"
25 
26 #include "gtkmm2ext/bindings.h"
27 #include "gtkmm2ext/visibility_tracker.h"
28 
29 #include "actions.h"
30 #include "ardour_dialog.h"
31 #include "ardour_ui.h"
32 #include "ardour_window.h"
33 #include "window_manager.h"
34 #include "processor_box.h"
35 
36 #include "pbd/i18n.h"
37 
38 using std::string;
39 using namespace WM;
40 using namespace PBD;
41 
42 Manager* Manager::_instance = 0;
43 
44 Manager&
instance()45 Manager::instance ()
46 {
47 	if (!_instance) {
48 		_instance = new Manager;
49 	}
50 	return *_instance;
51 }
52 
Manager()53 Manager::Manager ()
54 	: current_transient_parent (0)
55 {
56 }
57 
~Manager()58 Manager::~Manager ()
59 {
60 }
61 
62 void
register_window(ProxyBase * info)63 Manager::register_window (ProxyBase* info)
64 {
65 	_windows.push_back (info);
66 
67 	if (!info->menu_name().empty()) {
68 
69 		if (!window_actions) {
70 			window_actions = ActionManager::create_action_group (Gtkmm2ext::UI::instance()->global_bindings, X_("Window"));
71 		}
72 
73 		ActionManager::register_toggle_action (window_actions,
74 		                                       info->action_name().c_str(), info->menu_name().c_str(),
75 		                                       sigc::bind (sigc::mem_fun (*this, &Manager::toggle_window), info));
76 
77 		info->signal_map.connect (sigc::bind (sigc::mem_fun (*this, &Manager::window_proxy_was_mapped), info));
78 		info->signal_unmap.connect (sigc::bind (sigc::mem_fun (*this, &Manager::window_proxy_was_unmapped), info));
79 
80 	}
81 }
82 
83 void
window_proxy_was_mapped(ProxyBase * proxy)84 Manager::window_proxy_was_mapped (ProxyBase* proxy)
85 {
86 	Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (string_compose ("%1/%2", window_actions->get_name(), proxy->action_name()));
87 	if (!act) {
88 		return;
89 	}
90 	Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
91 	if (!tact) {
92 		return;
93 	}
94 
95 	tact->set_active (true);
96 }
97 
98 void
window_proxy_was_unmapped(ProxyBase * proxy)99 Manager::window_proxy_was_unmapped (ProxyBase* proxy)
100 {
101 	Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (string_compose ("%1/%2", window_actions->get_name(), proxy->action_name()));
102 	if (!act) {
103 		return;
104 	}
105 	Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
106 	if (!tact) {
107 		return;
108 	}
109 
110 	tact->set_active (false);
111 }
112 
113 void
remove(const ProxyBase * info)114 Manager::remove (const ProxyBase* info)
115 {
116 	for (Windows::iterator i = _windows.begin(); i != _windows.end(); ++i) {
117 		if ((*i) == info) {
118 			_windows.erase (i);
119 			return;
120 		}
121 	}
122 }
123 
124 void
toggle_window(ProxyBase * proxy)125 Manager::toggle_window (ProxyBase* proxy)
126 {
127 	Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (string_compose ("%1/%2", window_actions->get_name(), proxy->action_name()));
128 	if (!act) {
129 		return;
130 	}
131 	Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
132 	if (!tact) {
133 		return;
134 	}
135 
136 	if (tact->get_active()) {
137 		proxy->present ();
138 	} else {
139 		proxy->hide ();
140 	}
141 }
142 
143 void
show_visible() const144 Manager::show_visible() const
145 {
146 	for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
147 		if ((*i)->visible()) {
148 			Gtk::Window* win = (*i)->get (true);
149 			if (!win) {
150 				/* the window may be a plugin GUI for a plugin which
151 				 * is disabled or longer present.
152 				 */
153 				continue;
154 			}
155 			if (dynamic_cast<ArdourDialog*> (win)) {
156 				/* do not show dialogs at startup. Most
157 				 * dialogs require some signal connection work
158 				 * because we are trying to avoid recursive
159 				 * event loops (connecting instead to
160 				 * ::signal_response(). This means we need to
161 				 * destroy the window as well, so that the code
162 				 * which checks if it should be created will
163 				 * find that it is missing and will create it
164 				 * and connect to any necessary signals.
165 				 */
166 				(*i)->drop_window ();
167 				continue;
168 			}
169 			(*i)->show_all ();
170 			(*i)->present ();
171 		}
172 	}
173 }
174 
175 void
add_state(XMLNode & root) const176 Manager::add_state (XMLNode& root) const
177 {
178 	for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
179 		/* don't save state for temporary proxy windows
180 		 */
181 
182 		if (dynamic_cast<ProxyTemporary*> (*i)) {
183 			continue;
184 		}
185 
186 		root.add_child_nocopy ((*i)->get_state());
187 	}
188 }
189 
190 void
set_session(ARDOUR::Session * s)191 Manager::set_session (ARDOUR::Session* s)
192 {
193 	SessionHandlePtr::set_session (s);
194 	for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
195 		(*i)->set_session(s);
196 	}
197 }
198 
199 void
set_transient_for(Gtk::Window * parent)200 Manager::set_transient_for (Gtk::Window* parent)
201 {
202 	/* OS X has a richer concept of window layering than X does (or
203 	 * certainly, than any accepted conventions on X), and so the use of
204 	 * Manager::set_transient_for() is not necessary on that platform.
205 	 *
206 	 * On OS X this is mostly taken care of by using the window type rather
207 	 * than explicit 1:1 transient-for relationships.
208 	 */
209 
210 #ifndef __APPLE__
211 	if (parent) {
212 		for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
213 			Gtk::Window* win = (*i)->get();
214 			if (win) {
215 				win->set_transient_for (*parent);
216 			}
217 		}
218 	} else {
219 		for (Windows::const_iterator i = _windows.begin(); i != _windows.end(); ++i) {
220 			Gtk::Window* win = (*i)->get();
221 			if (win) {
222 				gtk_window_set_transient_for (win->gobj(), 0);
223 			}
224 		}
225 	}
226 
227 	current_transient_parent = parent;
228 #endif
229 }
230 
231 /*-------------------------*/
232 
ProxyBase(const std::string & name,const std::string & menu_name)233 ProxyBase::ProxyBase (const std::string& name, const std::string& menu_name)
234 	: WindowProxy (name, menu_name)
235 {
236 }
237 
ProxyBase(const std::string & name,const std::string & menu_name,const XMLNode & node)238 ProxyBase::ProxyBase (const std::string& name, const std::string& menu_name, const XMLNode& node)
239 	: WindowProxy (name, menu_name, node)
240 {
241 }
242 
243 void
setup()244 ProxyBase::setup ()
245 {
246 	WindowProxy::setup ();
247 	set_session(_session);
248 
249 }
250 
251 /*-----------------------*/
252 
ProxyTemporary(const string & name,Gtk::Window * win)253 ProxyTemporary::ProxyTemporary (const string& name, Gtk::Window* win)
254 	: ProxyBase (name, string())
255 {
256 	_window = win;
257 }
258 
259 ARDOUR::SessionHandlePtr*
session_handle()260 ProxyTemporary::session_handle()
261 {
262 	/* may return null */
263 	ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
264 	if (aw) { return aw; }
265 	ArdourDialog* ad = dynamic_cast<ArdourDialog*> (_window);
266 	if (ad) { return ad; }
267 	return 0;
268 }
269