lots of work to hide prelight from most buttons, etc. etc. etc; increase range of...
[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
60         VBox* box1;
61         box1 = manage (new VBox);
62         box1->pack_start (close_event_box, false, false, 2);
63         
64         window_box.pack_end (*box1, false, false, 2);
65
66         own_window.add_events (KEY_PRESS_MASK|KEY_RELEASE_MASK|BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK|POINTER_MOTION_MASK|POINTER_MOTION_HINT_MASK);
67         own_window.set_resizable (allow_resize);
68         own_window.set_type_hint (WINDOW_TYPE_HINT_TOOLBAR);
69
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 (sigc::mem_fun (*this, &TearOff::own_window_realized));
77         own_window.signal_configure_event().connect (sigc::mem_fun (*this, &TearOff::own_window_configured), false);
78
79         tearoff_arrow.set_name ("TearOffArrow");
80         close_arrow.set_name ("TearOffArrow");
81
82         VBox* box2;
83         box2 = manage (new VBox);
84         box2->pack_start (tearoff_event_box, false, false, 2);
85
86         pack_start (contents);
87         pack_start (*box2, false, false, 2);
88 }
89
90 TearOff::~TearOff ()
91 {
92 }
93
94 void
95 TearOff::set_can_be_torn_off (bool yn)
96 {
97         if (yn != _can_be_torn_off) {
98                 if (yn) {
99                         tearoff_arrow.set_no_show_all (false);
100                         tearoff_arrow.show ();
101                 } else {
102                         tearoff_arrow.set_no_show_all (true);
103                         tearoff_arrow.hide ();
104                 }
105                 _can_be_torn_off = yn;
106         }
107 }
108
109 void
110 TearOff::set_visible (bool yn)
111 {
112         /* don't change visibility if torn off */
113
114         if (own_window.is_visible()) {
115                 return;
116         }
117
118         if (_visible != yn) {
119                 _visible = yn;
120                 if (yn) {
121                         show_all();
122                         Visible ();
123                 } else {
124                         hide ();
125                         Hidden ();
126                 }
127         }
128 }
129
130 gint
131 TearOff::tearoff_click (GdkEventButton* /*ev*/)
132 {
133         tear_it_off ();
134         return true;
135 }
136
137 void
138 TearOff::tear_it_off ()
139 {
140         if (!_can_be_torn_off) {
141                 return;
142         }
143                 
144         if (torn_off()) {
145                 return;
146         }
147
148         remove (contents);
149         window_box.pack_start (contents);
150         own_window.set_name (get_name());
151         close_event_box.set_name (get_name());
152         own_window.show_all ();
153         own_window.present ();
154         hide ();
155         Detach ();
156 }        
157
158 gint
159 TearOff::close_click (GdkEventButton* /*ev*/)
160 {
161         put_it_back ();
162         return true;
163 }               
164
165 void
166 TearOff::put_it_back ()
167 {
168         if (!torn_off()) {
169                 return;
170         }
171
172         window_box.remove (contents);
173         pack_start (contents);
174         reorder_child (contents, 0);
175         own_window.hide ();
176         show_all ();
177         Attach ();
178 }
179
180 gint
181 TearOff::window_button_press (GdkEventButton* ev)
182 {
183         if (dragging || ev->button != 1) {
184                 dragging = false;
185                 own_window.remove_modal_grab();
186                 return true;
187         }
188
189         dragging = true;
190         drag_x = ev->x_root;
191         drag_y = ev->y_root;
192
193         own_window.add_modal_grab();
194
195         return true;
196 }
197
198 gint
199 TearOff::window_button_release (GdkEventButton* /*ev*/)
200 {
201         dragging = false;
202         own_window.remove_modal_grab();
203         return true;
204 }
205
206 gint
207 TearOff::window_delete_event (GdkEventAny* /*ev*/)
208 {
209         return close_click(0);
210 }
211
212 gint
213 TearOff::window_motion (GdkEventMotion* ev)
214 {
215         gint x;
216         gint y;
217         gint mx, my;
218         double x_delta;
219         double y_delta;
220         RefPtr<Gdk::Window> win (own_window.get_window());
221         
222         own_window.get_pointer (mx, my);
223
224         if (!dragging) {
225                 return true;
226         }
227
228         if (!(ev->state & GDK_BUTTON1_MASK)) {
229                 dragging = false;
230                 own_window.remove_modal_grab();
231                 return true;
232         }
233
234         x_delta = ev->x_root - drag_x;
235         y_delta = ev->y_root - drag_y;
236
237         win->get_root_origin (x, y);
238         win->move ((gint) floor (x + x_delta), (gint) floor (y + y_delta));
239         
240         drag_x = ev->x_root;
241         drag_y = ev->y_root;
242         
243         return true;
244 }
245
246 bool
247 TearOff::torn_off() const
248 {
249         return own_window.is_visible();
250 }
251
252 void
253 TearOff::add_state (XMLNode& node) const
254 {
255         node.add_property ("tornoff", (own_window.is_visible() ? "yes" : "no"));
256
257         if (own_window_width > 0) {
258                 char buf[32];
259
260                 snprintf (buf, sizeof (buf), "%d", own_window_width);
261                 node.add_property ("width", buf);
262                 snprintf (buf, sizeof (buf), "%d", own_window_height);
263                 node.add_property ("height", buf);
264                 snprintf (buf, sizeof (buf), "%d", own_window_xpos);
265                 node.add_property ("xpos", buf);
266                 snprintf (buf, sizeof (buf), "%d", own_window_ypos);
267                 node.add_property ("ypos", buf);
268         }
269 }        
270
271 void
272 TearOff::set_state (const XMLNode& node)
273 {
274         Glib::RefPtr<Gdk::Window> win;
275         const XMLProperty* prop;
276
277         if ((prop = node.property (X_("tornoff"))) == 0) {
278                 return;
279         }
280
281         if (prop->value() == "yes") {
282                 cerr << "Tearing off " << node.name() << endl;
283                 tear_it_off ();
284         } else {
285                 cerr << "Putting back " << node.name() << endl;
286                 put_it_back ();
287         }
288
289         if ((prop = node.property (X_("width"))) != 0) {
290                 sscanf (prop->value().c_str(), "%d", &own_window_width);
291         }
292         if ((prop = node.property (X_("height"))) != 0) {
293                 sscanf (prop->value().c_str(), "%d", &own_window_height);
294         }
295         if ((prop = node.property (X_("xpos"))) != 0) {
296                 sscanf (prop->value().c_str(), "%d", &own_window_xpos);
297         }
298         if ((prop = node.property (X_("ypos"))) != 0) {
299                 sscanf (prop->value().c_str(), "%d", &own_window_ypos);
300         }
301
302         if (own_window.is_realized()) {
303                 own_window.set_default_size (own_window_width, own_window_height);
304                 own_window.move (own_window_xpos, own_window_ypos);
305         }
306         /* otherwise do it once the window is realized, see below */
307 }        
308
309 void
310 TearOff::own_window_realized ()
311 {
312         cerr << "tearoff realized\n";
313
314         own_window.get_window()->set_decorations (WMDecoration (DECOR_BORDER|DECOR_RESIZEH));
315
316         if (own_window_width > 0) {
317                 own_window.set_default_size (own_window_width, own_window_height);
318                 own_window.move (own_window_xpos, own_window_ypos);
319         }
320 }
321
322 bool
323 TearOff::own_window_configured (GdkEventConfigure*)
324 {
325         Glib::RefPtr<const Gdk::Window> win;
326
327         win = own_window.get_window ();
328         
329         if (win) {
330                 win->get_size (own_window_width, own_window_height);
331                 win->get_position (own_window_xpos, own_window_ypos);
332         }
333
334         return false;
335 }
336
337 void
338 TearOff::hide_visible ()
339 {
340         if (torn_off()) {
341                 own_window.hide ();
342         }
343
344         hide ();
345 }
346
347