ee91b7e26607cc5ba2a83dfc96c6e84bf2be676f
[ardour.git] / libs / gtkmm2ext / window_proxy.cc
1 /*
2     Copyright (C) 2015 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
20 #include <gtkmm/action.h>
21 #include <gtkmm/window.h>
22
23 #include "pbd/convert.h"
24 #include "pbd/xml++.h"
25 #include "pbd/stacktrace.h"
26
27 #include "gtkmm2ext/window_proxy.h"
28 #include "gtkmm2ext/visibility_tracker.h"
29
30 #include "i18n.h"
31
32 using namespace Gtk;
33 using namespace Gtkmm2ext;
34 using namespace PBD;
35
36 WindowProxy::WindowProxy (const std::string& name)
37         : _name (name)
38         , _window (0)
39         , _visible (false)
40         , _x_off (-1)
41         , _y_off (-1)
42         , _width (-1)
43         , _height (-1)
44         , vistracker (0)
45         , _state_mask (StateMask (Position|Size))
46 {
47 }
48
49 WindowProxy::WindowProxy (const std::string& name, const std::string& menu_name)
50         : _name (name)
51         , _menu_name (menu_name)
52         , _window (0)
53         , _visible (false)
54         , _x_off (-1)
55         , _y_off (-1)
56         , _width (-1)
57         , _height (-1)
58         , vistracker (0)
59         , _state_mask (StateMask (Position|Size))
60 {
61 }
62
63 WindowProxy::WindowProxy (const std::string& name, const std::string& menu_name, const XMLNode& node)
64         : _name (name)
65         , _menu_name (menu_name)
66         , _window (0)
67         , _visible (false)
68         , _x_off (-1)
69         , _y_off (-1)
70         , _width (-1)
71         , _height (-1)
72         , vistracker (0)
73 {
74         set_state (node, 0);
75 }
76
77 WindowProxy::~WindowProxy ()
78 {
79         delete vistracker;
80         delete _window;
81 }
82
83 int
84 WindowProxy::set_state (const XMLNode& node, int /* version */)
85 {
86         XMLNodeList children = node.children ();
87         XMLNode const * child;
88         XMLNodeList::const_iterator i = children.begin ();
89
90         while (i != children.end()) {
91                 child = *i;
92                 XMLProperty const * prop = child->property (X_("name"));
93                 if (child->name() == X_("Window") && prop && prop->value() == _name) {
94                         break;
95                 }
96
97                 ++i;
98         }
99
100         if (i != children.end()) {
101
102                 XMLProperty const * prop;
103                 child = *i;
104
105                 if ((prop = child->property (X_("visible"))) != 0) {
106                         _visible = PBD::string_is_affirmative (prop->value ());
107                 }
108
109                 if ((prop = child->property (X_("x-off"))) != 0) {
110                         _x_off = atoi (prop->value());
111                 }
112                 if ((prop = child->property (X_("y-off"))) != 0) {
113                         _y_off = atoi (prop->value());
114                 }
115                 if ((prop = child->property (X_("x-size"))) != 0) {
116                         _width = atoi (prop->value());
117                 }
118                 if ((prop = child->property (X_("y-size"))) != 0) {
119                         _height = atoi (prop->value());
120                 }
121         }
122
123         if (_window) {
124                 setup ();
125         }
126
127         return 0;
128 }
129
130 void
131 WindowProxy::set_action (Glib::RefPtr<Gtk::Action> act)
132 {
133         _action = act;
134 }
135
136 std::string
137 WindowProxy::action_name() const
138 {
139         return string_compose (X_("toggle-%1"), _name);
140 }
141
142 void
143 WindowProxy::toggle()
144 {
145         if (!_window) {
146                 (void) get (true);
147                 setup ();
148                 assert (_window);
149                 /* XXX this is a hack - the window object should really
150                    ensure its components are all visible. sigh.
151                 */
152                 _window->show_all();
153                 /* we'd like to just call this and nothing else */
154                 _window->present ();
155         } else {
156                 if (_window->is_mapped()) {
157                         save_pos_and_size();
158                 }
159
160                 vistracker->cycle_visibility ();
161
162                 if (_window->is_mapped()) {
163                         if (_width != -1 && _height != -1) {
164                                 _window->set_default_size (_width, _height);
165                         }
166                         if (_x_off != -1 && _y_off != -1) {
167                                 _window->move (_x_off, _y_off);
168                         }
169                 }
170         }
171 }
172
173 std::string
174 WindowProxy::xml_node_name()
175 {
176         return X_("Window");
177 }
178
179 XMLNode&
180 WindowProxy::get_state ()
181 {
182         XMLNode* node = new XMLNode (xml_node_name());
183         char buf[32];
184
185         node->add_property (X_("name"), _name);
186
187         if (_window && vistracker) {
188
189                 /* we have a window, so use current state */
190
191                 _visible = vistracker->partially_visible ();
192                 _window->get_position (_x_off, _y_off);
193                 _window->get_size (_width, _height);
194         }
195
196         int x, y, w, h;
197
198         if (_state_mask & Position) {
199                 x = _x_off;
200                 y = _y_off;
201         } else {
202                 x = -1;
203                 y = -1;
204         }
205
206         if (_state_mask & Size) {
207                 w = _width;
208                 h = _height;
209         } else {
210                 w = -1;
211                 h = -1;
212         }
213
214         node->add_property (X_("visible"), _visible? X_("yes") : X_("no"));
215         snprintf (buf, sizeof (buf), "%d", x);
216         node->add_property (X_("x-off"), buf);
217         snprintf (buf, sizeof (buf), "%d", y);
218         node->add_property (X_("y-off"), buf);
219         snprintf (buf, sizeof (buf), "%d", w);
220         node->add_property (X_("x-size"), buf);
221         snprintf (buf, sizeof (buf), "%d", h);
222         node->add_property (X_("y-size"), buf);
223
224         return *node;
225 }
226
227 void
228 WindowProxy::drop_window ()
229 {
230         if (_window) {
231                 delete_connection.disconnect ();
232                 configure_connection.disconnect ();
233                 _window->hide ();
234                 delete _window;
235                 _window = 0;
236                 delete vistracker;
237                 vistracker = 0;
238         }
239 }
240
241 void
242 WindowProxy::use_window (Gtk::Window& win)
243 {
244         drop_window ();
245         _window = &win;
246         setup ();
247 }
248
249 void
250 WindowProxy::setup ()
251 {
252         assert (_window);
253
254         vistracker = new Gtkmm2ext::VisibilityTracker (*_window);
255
256         delete_connection = _window->signal_delete_event().connect (sigc::mem_fun (*this, &WindowProxy::delete_event_handler));
257         configure_connection = _window->signal_configure_event().connect (sigc::mem_fun (*this, &WindowProxy::configure_handler), false);
258
259         set_pos_and_size ();
260 }
261
262 bool
263 WindowProxy::configure_handler (GdkEventConfigure* ev)
264 {
265         /* stupidly, the geometry data in the event isn't the same as we get
266            from the window geometry APIs.so we have to actively interrogate
267            them to get the new information.
268
269            the difference is generally down to window manager framing.
270         */
271         save_pos_and_size ();
272         return false;
273 }
274
275
276 bool
277 WindowProxy::visible() const
278 {
279         if (vistracker) {
280                 /* update with current state */
281                 _visible = vistracker->partially_visible();
282         }
283         return _visible;
284 }
285
286 bool
287 WindowProxy::fully_visible () const
288 {
289         if (!vistracker) {
290                 /* no vistracker .. no window .. cannot be fully visible */
291                 return false;
292         }
293         return vistracker->fully_visible();
294 }
295
296 void
297 WindowProxy::show ()
298 {
299         get (true);
300         assert (_window);
301         _window->show ();
302 }
303
304 void
305 WindowProxy::maybe_show ()
306 {
307         if (_visible) {
308                 show ();
309         }
310 }
311
312 void
313 WindowProxy::show_all ()
314 {
315         get (true);
316         assert (_window);
317         _window->show_all ();
318 }
319
320 void
321 WindowProxy::present ()
322 {
323         get (true);
324         assert (_window);
325
326         _window->show_all ();
327         _window->present ();
328
329         /* turn off any mouse-based positioning */
330         _window->set_position (Gtk::WIN_POS_NONE);
331 }
332
333 void
334 WindowProxy::hide ()
335 {
336         if (_window) {
337                 save_pos_and_size();
338                 _window->hide ();
339         }
340 }
341
342 bool
343 WindowProxy::delete_event_handler (GdkEventAny* /*ev*/)
344 {
345         if (_action) {
346                 _action->activate ();
347         } else {
348                 hide();
349         }
350
351         return true;
352 }
353
354 void
355 WindowProxy::save_pos_and_size ()
356 {
357         if (_window) {
358                 _window->get_position (_x_off, _y_off);
359                 _window->get_size (_width, _height);
360         }
361 }
362
363 void
364 WindowProxy::set_pos_and_size ()
365 {
366         if (!_window) {
367                 return;
368         }
369
370         if ((_state_mask & Position) && (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1)) {
371                 /* cancel any mouse-based positioning */
372                 _window->set_position (Gtk::WIN_POS_NONE);
373         }
374
375         if ((_state_mask & Size) && _width != -1 && _height != -1) {
376                 _window->resize (_width, _height);
377         }
378
379         if ((_state_mask & Position) && _x_off != -1 && _y_off != -1) {
380                 _window->move (_x_off, _y_off);
381         }
382 }
383
384 void
385 WindowProxy::set_pos ()
386 {
387         if (!_window) {
388                 return;
389         }
390
391         if (!(_state_mask & Position)) {
392                 return;
393         }
394
395         if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
396                 /* cancel any mouse-based positioning */
397                 _window->set_position (Gtk::WIN_POS_NONE);
398         }
399
400         if (_x_off != -1 && _y_off != -1) {
401                 _window->move (_x_off, _y_off);
402         }
403 }
404
405 void
406 WindowProxy::set_state_mask (StateMask sm)
407 {
408         _state_mask = sm;
409 }