enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[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 "pbd/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                 map_connection.disconnect ();
234                 unmap_connection.disconnect ();
235                 _window->hide ();
236                 delete _window;
237                 _window = 0;
238                 delete vistracker;
239                 vistracker = 0;
240         }
241 }
242
243 void
244 WindowProxy::use_window (Gtk::Window& win)
245 {
246         drop_window ();
247         _window = &win;
248         setup ();
249 }
250
251 void
252 WindowProxy::setup ()
253 {
254         assert (_window);
255
256         vistracker = new Gtkmm2ext::VisibilityTracker (*_window);
257
258         delete_connection = _window->signal_delete_event().connect (sigc::mem_fun (*this, &WindowProxy::delete_event_handler));
259         configure_connection = _window->signal_configure_event().connect (sigc::mem_fun (*this, &WindowProxy::configure_handler), false);
260         map_connection = _window->signal_map().connect (sigc::mem_fun (*this, &WindowProxy::map_handler), false);
261         unmap_connection = _window->signal_unmap().connect (sigc::mem_fun (*this, &WindowProxy::unmap_handler), false);
262
263         set_pos_and_size ();
264 }
265
266 void
267 WindowProxy::map_handler ()
268 {
269         /* emit our own signal */
270         signal_map ();
271 }
272
273 void
274 WindowProxy::unmap_handler ()
275 {
276         /* emit out own signal */
277         signal_unmap ();
278 }
279
280 bool
281 WindowProxy::configure_handler (GdkEventConfigure* ev)
282 {
283         /* stupidly, the geometry data in the event isn't the same as we get
284            from the window geometry APIs.so we have to actively interrogate
285            them to get the new information.
286
287            the difference is generally down to window manager framing.
288         */
289         save_pos_and_size ();
290         return false;
291 }
292
293
294 bool
295 WindowProxy::visible() const
296 {
297         if (vistracker) {
298                 /* update with current state */
299                 _visible = vistracker->partially_visible();
300         }
301         return _visible;
302 }
303
304 bool
305 WindowProxy::fully_visible () const
306 {
307         if (!vistracker) {
308                 /* no vistracker .. no window .. cannot be fully visible */
309                 return false;
310         }
311         return vistracker->fully_visible();
312 }
313
314 void
315 WindowProxy::show ()
316 {
317         get (true);
318         assert (_window);
319         _window->show ();
320 }
321
322 void
323 WindowProxy::maybe_show ()
324 {
325         if (_visible) {
326                 show ();
327         }
328 }
329
330 void
331 WindowProxy::show_all ()
332 {
333         get (true);
334         assert (_window);
335         _window->show_all ();
336 }
337
338 void
339 WindowProxy::present ()
340 {
341         get (true);
342         assert (_window);
343
344         _window->show_all ();
345         _window->present ();
346
347         /* turn off any mouse-based positioning */
348         _window->set_position (Gtk::WIN_POS_NONE);
349 }
350
351 void
352 WindowProxy::hide ()
353 {
354         if (_window) {
355                 save_pos_and_size();
356                 _window->hide ();
357         }
358 }
359
360 bool
361 WindowProxy::delete_event_handler (GdkEventAny* /*ev*/)
362 {
363         if (_action) {
364                 _action->activate ();
365         } else {
366                 hide();
367         }
368
369         return true;
370 }
371
372 void
373 WindowProxy::save_pos_and_size ()
374 {
375         if (_window) {
376                 _window->get_position (_x_off, _y_off);
377                 _window->get_size (_width, _height);
378         }
379 }
380
381 void
382 WindowProxy::set_pos_and_size ()
383 {
384         if (!_window) {
385                 return;
386         }
387
388         if ((_state_mask & Position) && (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1)) {
389                 /* cancel any mouse-based positioning */
390                 _window->set_position (Gtk::WIN_POS_NONE);
391         }
392
393         if ((_state_mask & Size) && _width != -1 && _height != -1) {
394                 _window->resize (_width, _height);
395         }
396
397         if ((_state_mask & Position) && _x_off != -1 && _y_off != -1) {
398                 _window->move (_x_off, _y_off);
399         }
400 }
401
402 void
403 WindowProxy::set_pos ()
404 {
405         if (!_window) {
406                 return;
407         }
408
409         if (!(_state_mask & Position)) {
410                 return;
411         }
412
413         if (_width != -1 || _height != -1 || _x_off != -1 || _y_off != -1) {
414                 /* cancel any mouse-based positioning */
415                 _window->set_position (Gtk::WIN_POS_NONE);
416         }
417
418         if (_x_off != -1 && _y_off != -1) {
419                 _window->move (_x_off, _y_off);
420         }
421 }
422
423 void
424 WindowProxy::set_state_mask (StateMask sm)
425 {
426         _state_mask = sm;
427 }