update german translation
[ardour.git] / libs / gtkmm2ext / tabbable.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/notebook.h>
22 #include <gtkmm/window.h>
23 #include <gtkmm/stock.h>
24
25 #include "gtkmm2ext/tabbable.h"
26 #include "gtkmm2ext/gtk_ui.h"
27 #include "gtkmm2ext/utils.h"
28 #include "gtkmm2ext/visibility_tracker.h"
29
30 #include "pbd/stacktrace.h"
31
32 #include "pbd/i18n.h"
33
34 using namespace Gtkmm2ext;
35 using namespace Gtk;
36 using std::string;
37
38 Tabbable::Tabbable (Widget& w, const string& name, bool tabbed_by_default)
39         : WindowProxy (name)
40         , _contents (w)
41         , _parent_notebook (0)
42         , tab_requested_by_state (tabbed_by_default)
43 {
44 }
45
46 Tabbable::~Tabbable ()
47 {
48         if (_window) {
49                 delete _window;
50                 _window = 0;
51         }
52 }
53
54 void
55 Tabbable::add_to_notebook (Notebook& notebook, const string& tab_title)
56 {
57         _parent_notebook = &notebook;
58
59         if (tab_requested_by_state) {
60                 attach ();
61         }
62 }
63
64 Window*
65 Tabbable::use_own_window (bool and_pack_it)
66 {
67         Gtk::Window* win = get (true);
68
69         if (and_pack_it) {
70                 Gtk::Container* parent = _contents.get_parent();
71                 if (parent) {
72                         _contents.hide ();
73                         parent->remove (_contents);
74                 }
75                 _own_notebook.append_page (_contents);
76                 _contents.show ();
77         }
78
79         return win;
80
81 }
82
83 bool
84 Tabbable::window_visible () const
85 {
86         if (!_window) {
87                 return false;
88         }
89
90         return _window->is_visible();
91 }
92
93 Window*
94 Tabbable::get (bool create)
95 {
96         if (_window) {
97                 return _window;
98         }
99
100         if (!create) {
101                 return 0;
102         }
103
104         /* From here on, we're creating the window
105          */
106
107         if ((_window = new Window (WINDOW_TOPLEVEL)) == 0) {
108                 return 0;
109         }
110
111         _window->add (_own_notebook);
112         _own_notebook.show ();
113         _own_notebook.set_show_tabs (false);
114
115         _window->signal_map().connect (sigc::mem_fun (*this, &Tabbable::window_mapped));
116         _window->signal_unmap().connect (sigc::mem_fun (*this, &Tabbable::window_unmapped));
117
118         /* do other window-related setup */
119
120         setup ();
121
122         /* window should be ready for derived classes to do something with it */
123
124         return _window;
125 }
126
127 void
128 Tabbable::show_own_window (bool and_pack_it)
129 {
130         Gtk::Widget* parent = _contents.get_parent();
131         Gtk::Allocation alloc;
132
133         if (parent) {
134                 alloc = parent->get_allocation();
135         }
136
137         (void) use_own_window (and_pack_it);
138
139         if (parent) {
140                 _window->set_default_size (alloc.get_width(), alloc.get_height());
141         }
142
143         tab_requested_by_state = false;
144
145         _window->present ();
146 }
147
148 Gtk::Notebook*
149 Tabbable::tab_root_drop ()
150 {
151         /* This is called after a drop of a tab onto the root window. Its
152          * responsibility xois to return the notebook that this Tabbable's
153          * contents should be packed into before the drop handling is
154          * completed. It is not responsible for actually taking care of this
155          * packing.
156          */
157
158         show_own_window (false);
159         return &_own_notebook;
160 }
161
162 void
163 Tabbable::show_window ()
164 {
165         make_visible ();
166
167         if (_window && (current_toplevel() == _window)) {
168                 if (!_visible) { /* was hidden, update status */
169                         set_pos_and_size ();
170                 }
171         }
172 }
173
174 /** If this Tabbable is currently parented by a tab, ensure that the tab is the
175  * current one. If it is parented by a window, then toggle the visibility of
176  * that window.
177  */
178 void
179 Tabbable::change_visibility ()
180 {
181         if (tabbed()) {
182                 _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
183                 return;
184         }
185
186         if (tab_requested_by_state) {
187                 /* should be tabbed, but currently isn't parented by a notebook */
188                 return;
189         }
190
191         if (_window && (current_toplevel() == _window)) {
192                 /* Use WindowProxy method which will rotate then hide */
193                 toggle();
194         }
195 }
196
197 void
198 Tabbable::make_visible ()
199 {
200         if (_window && (current_toplevel() == _window)) {
201                 set_pos ();
202                 _window->present ();
203         } else {
204
205                 if (!tab_requested_by_state) {
206                         show_own_window (true);
207                 } else {
208                         show_tab ();
209                 }
210         }
211 }
212
213 void
214 Tabbable::make_invisible ()
215 {
216         if (_window && (current_toplevel() == _window)) {
217                 _window->hide ();
218         } else {
219                 hide_tab ();
220         }
221 }
222
223 void
224 Tabbable::detach ()
225 {
226         show_own_window (true);
227 }
228
229 void
230 Tabbable::attach ()
231 {
232         if (!_parent_notebook) {
233                 return;
234         }
235
236         if (tabbed()) {
237                 /* already tabbed */
238                 return;
239         }
240
241
242         if (_window && current_toplevel() == _window) {
243                 /* unpack Tabbable from parent, put it back in the main tabbed
244                  * notebook
245                  */
246
247                 save_pos_and_size ();
248
249                 _contents.hide ();
250                 _contents.get_parent()->remove (_contents);
251
252                 /* leave the window around */
253
254                 _window->hide ();
255         }
256
257         _parent_notebook->append_page (_contents);
258         _parent_notebook->set_tab_detachable (_contents);
259         _parent_notebook->set_tab_reorderable (_contents);
260         _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
261         _contents.show ();
262
263         /* have to force this on, which is semantically correct, since
264          * the user has effectively asked for it.
265          */
266
267         tab_requested_by_state = true;
268         StateChange (*this);
269 }
270
271 bool
272 Tabbable::delete_event_handler (GdkEventAny *ev)
273 {
274         _window->hide();
275
276         return true;
277 }
278
279 bool
280 Tabbable::tabbed () const
281 {
282         if (_window && (current_toplevel() == _window)) {
283                 return false;
284         }
285
286         if (_parent_notebook && (_parent_notebook->page_num (_contents) >= 0)) {
287                 return true;
288         }
289
290         return false;
291 }
292
293 void
294 Tabbable::hide_tab ()
295 {
296         if (tabbed()) {
297                 _contents.hide();
298                 _parent_notebook->remove_page (_contents);
299                 StateChange (*this);
300         }
301 }
302
303 void
304 Tabbable::show_tab ()
305 {
306         if (!window_visible() && _parent_notebook) {
307                 if (_contents.get_parent() == 0) {
308                         tab_requested_by_state = true;
309                         add_to_notebook (*_parent_notebook, _tab_title);
310                 }
311                 _parent_notebook->set_current_page (_parent_notebook->page_num (_contents));
312                 _contents.show ();
313                 current_toplevel()->present ();
314         }
315 }
316
317 Gtk::Window*
318 Tabbable::current_toplevel () const
319 {
320         return dynamic_cast<Gtk::Window*> (contents().get_toplevel());
321 }
322
323 string
324 Tabbable::xml_node_name()
325 {
326         return WindowProxy::xml_node_name();
327 }
328
329 bool
330 Tabbable::tabbed_by_default() const
331 {
332         return tab_requested_by_state;
333 }
334
335 XMLNode&
336 Tabbable::get_state()
337 {
338         XMLNode& node (WindowProxy::get_state());
339
340         node.set_property (X_("tabbed"),  tabbed());
341
342         return node;
343 }
344
345 int
346 Tabbable::set_state (const XMLNode& node, int version)
347 {
348         int ret;
349
350         if ((ret = WindowProxy::set_state (node, version)) != 0) {
351                 return ret;
352         }
353
354         if (_visible) {
355                 show_own_window (true);
356         }
357
358         XMLNodeList children = node.children ();
359         XMLNode* window_node = node.child ("Window");
360
361         if (window_node) {
362                 window_node->get_property (X_("tabbed"), tab_requested_by_state);
363         }
364
365         if (!_visible) {
366                 if (tab_requested_by_state) {
367                         attach ();
368                 } else {
369                         /* this does nothing if not tabbed */
370                         hide_tab ();
371                 }
372         }
373
374         return ret;
375 }
376
377 void
378 Tabbable::window_mapped ()
379 {
380         StateChange (*this);
381 }
382
383 void
384 Tabbable::window_unmapped ()
385 {
386         StateChange (*this);
387 }