improve size and positioning of Tabbable's after being torn off, hidden, reshown...
[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 {
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 {
59 }
60
61 WindowProxy::WindowProxy (const std::string& name, const std::string& menu_name, const XMLNode& node)
62         : _name (name)
63         , _menu_name (menu_name)
64         , _window (0)
65         , _visible (false)
66         , _x_off (-1)
67         , _y_off (-1)
68         , _width (-1)
69         , _height (-1)
70         , vistracker (0)
71 {
72         set_state (node, 0);
73 }
74
75 WindowProxy::~WindowProxy ()
76 {
77         delete vistracker;
78         delete _window;
79 }
80
81 int
82 WindowProxy::set_state (const XMLNode& node, int /* version */)
83 {
84         XMLNodeList children = node.children ();
85
86         XMLNodeList::const_iterator i = children.begin ();
87
88         while (i != children.end()) {
89                 XMLProperty* prop = (*i)->property (X_("name"));
90                 if ((*i)->name() == X_("Window") && prop && prop->value() == _name) {
91                         break;
92                 }
93
94                 ++i;
95         }
96
97         if (i != children.end()) {
98
99                 XMLProperty* prop;
100
101                 if ((prop = (*i)->property (X_("visible"))) != 0) {
102                         _visible = PBD::string_is_affirmative (prop->value ());
103                 }
104
105                 if ((prop = (*i)->property (X_("x-off"))) != 0) {
106                         _x_off = atoi (prop->value());
107                 }
108                 if ((prop = (*i)->property (X_("y-off"))) != 0) {
109                         _y_off = atoi (prop->value());
110                 }
111                 if ((prop = (*i)->property (X_("x-size"))) != 0) {
112                         _width = atoi (prop->value());
113                 }
114                 if ((prop = (*i)->property (X_("y-size"))) != 0) {
115                         _height = atoi (prop->value());
116                 }
117         }
118
119         if (_window) {
120                 setup ();
121         }
122
123         return 0;
124 }
125
126 void
127 WindowProxy::set_action (Glib::RefPtr<Gtk::Action> act)
128 {
129         _action = act;
130 }
131
132 std::string
133 WindowProxy::action_name() const
134 {
135         return string_compose (X_("toggle-%1"), _name);
136 }
137
138 void
139 WindowProxy::toggle()
140 {
141         if (!_window) {
142                 (void) get (true);
143                 setup ();
144                 assert (_window);
145                 /* XXX this is a hack - the window object should really
146                    ensure its components are all visible. sigh.
147                 */
148                 _window->show_all();
149                 /* we'd like to just call this and nothing else */
150                 _window->present ();
151         } else {
152                 if (_window->is_mapped()) {
153                         save_pos_and_size();
154                 }
155
156                 vistracker->cycle_visibility ();
157
158                 if (_window->is_mapped()) {
159                         if (_width != -1 && _height != -1) {
160                                 _window->set_default_size (_width, _height);
161                         }
162                         if (_x_off != -1 && _y_off != -1) {
163                                 _window->move (_x_off, _y_off);
164                         }
165                 }
166         }
167 }
168
169 std::string
170 WindowProxy::xml_node_name()
171 {
172         return X_("Window");
173 }
174
175 XMLNode&
176 WindowProxy::get_state ()
177 {
178         XMLNode* node = new XMLNode (xml_node_name());
179         char buf[32];
180
181         node->add_property (X_("name"), _name);
182
183         if (_window && vistracker) {
184
185                 /* we have a window, so use current state */
186
187                 _visible = vistracker->partially_visible ();
188                 _window->get_position (_x_off, _y_off);
189                 _window->get_size (_width, _height);
190         }
191
192         node->add_property (X_("visible"), _visible? X_("yes") : X_("no"));
193         snprintf (buf, sizeof (buf), "%d", _x_off);
194         node->add_property (X_("x-off"), buf);
195         snprintf (buf, sizeof (buf), "%d", _y_off);
196         node->add_property (X_("y-off"), buf);
197         snprintf (buf, sizeof (buf), "%d", _width);
198         node->add_property (X_("x-size"), buf);
199         snprintf (buf, sizeof (buf), "%d", _height);
200         node->add_property (X_("y-size"), buf);
201
202         return *node;
203 }
204
205 void
206 WindowProxy::drop_window ()
207 {
208         if (_window) {
209                 _window->hide ();
210                 delete _window;
211                 _window = 0;
212                 delete vistracker;
213                 vistracker = 0;
214         }
215 }
216
217 void
218 WindowProxy::use_window (Gtk::Window& win)
219 {
220         drop_window ();
221         _window = &win;
222         setup ();
223 }
224
225 void
226 WindowProxy::setup ()
227 {
228         assert (_window);
229
230         vistracker = new Gtkmm2ext::VisibilityTracker (*_window);
231         _window->signal_delete_event().connect (sigc::mem_fun (*this, &WindowProxy::delete_event_handler));
232
233         set_pos_and_size ();
234 }
235
236 bool
237 WindowProxy::visible() const
238 {
239         if (vistracker) {
240                 /* update with current state */
241                 _visible = vistracker->partially_visible();
242         }
243         return _visible;
244 }
245
246 bool
247 WindowProxy::fully_visible () const
248 {
249         if (!vistracker) {
250                 /* no vistracker .. no window .. cannot be fully visible */
251                 return false;
252         }
253         return vistracker->fully_visible();
254 }
255
256 void
257 WindowProxy::show ()
258 {
259         get (true);
260         assert (_window);
261         _window->show ();
262 }
263
264 void
265 WindowProxy::maybe_show ()
266 {
267         if (_visible) {
268                 show ();
269         }
270 }
271
272 void
273 WindowProxy::show_all ()
274 {
275         get (true);
276         assert (_window);
277         _window->show_all ();
278 }
279
280 void
281 WindowProxy::present ()
282 {
283         get (true);
284         assert (_window);
285
286         _window->show_all ();
287         _window->present ();
288
289         /* turn off any mouse-based positioning */
290         _window->set_position (Gtk::WIN_POS_NONE);
291 }
292
293 void
294 WindowProxy::hide ()
295 {
296         if (_window) {
297                 save_pos_and_size();
298                 _window->hide ();
299         }
300 }
301
302 bool
303 WindowProxy::delete_event_handler (GdkEventAny* /*ev*/)
304 {
305         if (_action) {
306                 _action->activate ();
307         } else {
308                 hide();
309         }
310
311         return true;
312 }
313
314 void
315 WindowProxy::save_pos_and_size ()
316 {
317         if (_window) {
318                 _window->get_position (_x_off, _y_off);
319                 _window->get_size (_width, _height);
320         }
321 }
322
323 void
324 WindowProxy::set_pos_and_size ()
325 {
326         if (!_window) {
327                 return;
328         }
329
330         if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
331                 /* cancel any mouse-based positioning */
332                 _window->set_position (Gtk::WIN_POS_NONE);
333         }
334
335         if (_width != -1 && _height != -1) {
336                 _window->resize (_width, _height);
337         }
338
339         if (_x_off != -1 && _y_off != -1) {
340                 _window->move (_x_off, _y_off);
341         }
342 }
343
344 void
345 WindowProxy::set_pos ()
346 {
347         if (!_window) {
348                 return;
349         }
350
351         if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
352                 /* cancel any mouse-based positioning */
353                 _window->set_position (Gtk::WIN_POS_NONE);
354         }
355
356         if (_x_off != -1 && _y_off != -1) {
357                 _window->move (_x_off, _y_off);
358         }
359 }