save tearoff state; restore monitor section state reasonably well; fixup access contr...
[ardour.git] / libs / gtkmm2ext / tearoff.cc
1 /*
2     Copyright (C) 2003 Paul Barton-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     $Id$
19 */
20
21 #include <cmath>
22 #include <iostream>
23
24 #include "pbd/xml++.h"
25
26 #include "gtkmm2ext/tearoff.h"
27 #include "gtkmm2ext/utils.h"
28
29 #include "i18n.h"
30
31 using namespace Gtkmm2ext;
32 using namespace Gtk;
33 using namespace Gdk;
34 using namespace Glib;
35 using namespace std;
36
37 TearOff::TearOff (Widget& c, bool allow_resize)
38         : contents (c),
39           own_window (Gtk::WINDOW_TOPLEVEL),
40           tearoff_arrow (ARROW_DOWN, SHADOW_OUT),
41           close_arrow (ARROW_UP, SHADOW_OUT)
42 {
43         dragging = false;
44         _visible = true;
45         _can_be_torn_off = true;
46         own_window_width = 0;
47         own_window_height = 0;
48         own_window_xpos = 0;
49         own_window_ypos = 0;
50
51         tearoff_event_box.add (tearoff_arrow);
52         tearoff_event_box.set_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK);
53         tearoff_event_box.signal_button_release_event().connect (mem_fun (*this, &TearOff::tearoff_click));
54
55         close_event_box.add (close_arrow);
56         close_event_box.set_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK);
57         close_event_box.signal_button_release_event().connect (mem_fun (*this, &TearOff::close_click));
58         
59         own_window.add_events (KEY_PRESS_MASK|KEY_RELEASE_MASK|BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK|POINTER_MOTION_MASK|POINTER_MOTION_HINT_MASK);
60         own_window.set_resizable (allow_resize);
61         own_window.set_type_hint (WINDOW_TYPE_HINT_TOOLBAR);
62         own_window.signal_realize().connect (sigc::mem_fun (*this, &TearOff::own_window_realized));
63         own_window.signal_configure_event().connect (sigc::mem_fun (*this, &TearOff::own_window_configured), false);
64
65         VBox* box1;
66         box1 = manage (new VBox);
67         box1->pack_start (close_event_box, false, false, 2);
68         
69         window_box.pack_end (*box1, false, false, 2);
70         own_window.add (window_box);
71         
72         own_window.signal_button_press_event().connect (mem_fun (*this, &TearOff::window_button_press));
73         own_window.signal_button_release_event().connect (mem_fun (*this, &TearOff::window_button_release));
74         own_window.signal_motion_notify_event().connect (mem_fun (*this, &TearOff::window_motion));
75         own_window.signal_delete_event().connect (mem_fun (*this, &TearOff::window_delete_event));
76         own_window.signal_realize().connect (bind (sigc::ptr_fun (Gtkmm2ext::set_decoration), &own_window, WMDecoration (DECOR_BORDER|DECOR_RESIZEH)));
77
78         tearoff_arrow.set_name ("TearOffArrow");
79         close_arrow.set_name ("TearOffArrow");
80
81         VBox* box2;
82         box2 = manage (new VBox);
83         box2->pack_start (tearoff_event_box, false, false, 2);
84
85         pack_start (contents);
86         pack_start (*box2, false, false, 2);
87 }
88
89 TearOff::~TearOff ()
90 {
91 }
92
93 void
94 TearOff::set_can_be_torn_off (bool yn)
95 {
96         if (yn != _can_be_torn_off) {
97                 if (yn) {
98                         tearoff_arrow.set_no_show_all (false);
99                         tearoff_arrow.show ();
100                 } else {
101                         tearoff_arrow.set_no_show_all (true);
102                         tearoff_arrow.hide ();
103                 }
104                 _can_be_torn_off = yn;
105         }
106 }
107
108 void
109 TearOff::set_visible (bool yn)
110 {
111         /* don't change visibility if torn off */
112
113         if (own_window.is_visible()) {
114                 return;
115         }
116
117         if (_visible != yn) {
118                 _visible = yn;
119                 if (yn) {
120                         show_all();
121                         Visible ();
122                 } else {
123                         hide ();
124                         Hidden ();
125                 }
126         }
127 }
128
129 gint
130 TearOff::tearoff_click (GdkEventButton* /*ev*/)
131 {
132         tear_it_off ();
133         return true;
134 }
135
136 void
137 TearOff::tear_it_off ()
138 {
139         if (!_can_be_torn_off) {
140                 return;
141         }
142                 
143         if (torn_off()) {
144                 return;
145         }
146
147         remove (contents);
148         window_box.pack_start (contents);
149         own_window.set_name (get_name());
150         close_event_box.set_name (get_name());
151         own_window.show_all ();
152         own_window.present ();
153         hide ();
154         Detach ();
155 }        
156
157 gint
158 TearOff::close_click (GdkEventButton* /*ev*/)
159 {
160         put_it_back ();
161         return true;
162 }               
163
164 void
165 TearOff::put_it_back ()
166 {
167         if (!torn_off()) {
168                 return;
169         }
170
171         window_box.remove (contents);
172         pack_start (contents);
173         reorder_child (contents, 0);
174         own_window.hide ();
175         show_all ();
176         Attach ();
177 }
178
179 gint
180 TearOff::window_button_press (GdkEventButton* ev)
181 {
182         if (dragging || ev->button != 1) {
183                 dragging = false;
184                 own_window.remove_modal_grab();
185                 return true;
186         }
187
188         dragging = true;
189         drag_x = ev->x_root;
190         drag_y = ev->y_root;
191
192         own_window.add_modal_grab();
193
194         return true;
195 }
196
197 gint
198 TearOff::window_button_release (GdkEventButton* /*ev*/)
199 {
200         dragging = false;
201         own_window.remove_modal_grab();
202         return true;
203 }
204
205 gint
206 TearOff::window_delete_event (GdkEventAny* /*ev*/)
207 {
208         return close_click(0);
209 }
210
211 gint
212 TearOff::window_motion (GdkEventMotion* ev)
213 {
214         gint x;
215         gint y;
216         gint mx, my;
217         double x_delta;
218         double y_delta;
219         RefPtr<Gdk::Window> win (own_window.get_window());
220         
221         own_window.get_pointer (mx, my);
222
223         if (!dragging) {
224                 return true;
225         }
226
227         if (!(ev->state & GDK_BUTTON1_MASK)) {
228                 dragging = false;
229                 own_window.remove_modal_grab();
230                 return true;
231         }
232
233         x_delta = ev->x_root - drag_x;
234         y_delta = ev->y_root - drag_y;
235
236         win->get_root_origin (x, y);
237         win->move ((gint) floor (x + x_delta), (gint) floor (y + y_delta));
238         
239         drag_x = ev->x_root;
240         drag_y = ev->y_root;
241         
242         return true;
243 }
244
245 bool
246 TearOff::torn_off() const
247 {
248         return own_window.is_visible();
249 }
250
251 void
252 TearOff::add_tornoff_state (XMLNode& node) const
253 {
254         node.add_property ("tornoff", (own_window.is_visible() ? "yes" : "no"));
255
256         if (own_window_width > 0) {
257                 char buf[32];
258
259                 snprintf (buf, sizeof (buf), "%d", own_window_width);
260                 node.add_property ("width", buf);
261                 snprintf (buf, sizeof (buf), "%d", own_window_height);
262                 node.add_property ("height", buf);
263                 snprintf (buf, sizeof (buf), "%d", own_window_xpos);
264                 node.add_property ("xpos", buf);
265                 snprintf (buf, sizeof (buf), "%d", own_window_ypos);
266                 node.add_property ("ypos", buf);
267         }
268 }        
269
270 void
271 TearOff::set_tornoff_state (const XMLNode& node)
272 {
273         Glib::RefPtr<Gdk::Window> win;
274         const XMLProperty* prop;
275
276         if ((prop = node.property (X_("tornoff"))) == 0) {
277                 return;
278         }
279
280         if (prop->value() == "yes") {
281                 tear_it_off ();
282         } else {
283                 put_it_back ();
284         }
285
286         if ((prop = node.property (X_("width"))) != 0) {
287                 sscanf (prop->value().c_str(), "%d", &own_window_width);
288         }
289         if ((prop = node.property (X_("height"))) != 0) {
290                 sscanf (prop->value().c_str(), "%d", &own_window_height);
291         }
292         if ((prop = node.property (X_("xpos"))) != 0) {
293                 sscanf (prop->value().c_str(), "%d", &own_window_xpos);
294         }
295         if ((prop = node.property (X_("ypos"))) != 0) {
296                 sscanf (prop->value().c_str(), "%d", &own_window_ypos);
297         }
298
299         own_window.set_default_size (own_window_width, own_window_height);
300         own_window.move (own_window_xpos, own_window_ypos);
301 }        
302
303 void
304 TearOff::own_window_realized ()
305 {
306         if (own_window_width > 0) {
307                 own_window.set_default_size (own_window_width, own_window_height);
308                 own_window.move (own_window_xpos, own_window_ypos);
309         }
310 }
311
312 bool
313 TearOff::own_window_configured (GdkEventConfigure*)
314 {
315         Glib::RefPtr<const Gdk::Window> win;
316
317         win = own_window.get_window ();
318         
319         if (win) {
320                 win->get_size (own_window_width, own_window_height);
321                 win->get_position (own_window_xpos, own_window_ypos);
322         }
323
324         return false;
325 }