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