Remove ambiguous API implementation
[ardour.git] / libs / widgets / 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 */
19
20 #include <cmath>
21 #include <iostream>
22
23 #include "pbd/xml++.h"
24
25 #include "gtkmm2ext/utils.h"
26
27 #include "widgets/tearoff.h"
28
29 #include "pbd/i18n.h"
30
31 using namespace std;
32 using namespace Glib;
33 using namespace Gdk;
34 using namespace Gtk;
35 using namespace ArdourWidgets;
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         , dragging (false)
43         , _visible (true)
44         , _torn (false)
45         , _can_be_torn_off (true)
46
47 {
48         own_window_width = 0;
49         own_window_height = 0;
50         own_window_xpos = 0;
51         own_window_ypos = 0;
52
53         tearoff_event_box.add (tearoff_arrow);
54         tearoff_event_box.set_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK);
55         tearoff_event_box.signal_button_release_event().connect (mem_fun (*this, &TearOff::tearoff_click));
56
57         tearoff_event_box.set_tooltip_text (_("Click to tear this into its own window"));
58
59         close_event_box.add (close_arrow);
60         close_event_box.set_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK);
61         close_event_box.signal_button_release_event().connect (mem_fun (*this, &TearOff::close_click));
62
63         close_event_box.set_tooltip_text (_("Click to put this back in the main window"));
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
71         own_window.add_events (KEY_PRESS_MASK|KEY_RELEASE_MASK|BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK|POINTER_MOTION_MASK|POINTER_MOTION_HINT_MASK);
72         own_window.set_resizable (allow_resize);
73         own_window.set_type_hint (WINDOW_TYPE_HINT_UTILITY);
74
75         own_window.add (window_box);
76
77         own_window.signal_button_press_event().connect (mem_fun (*this, &TearOff::window_button_press));
78         own_window.signal_button_release_event().connect (mem_fun (*this, &TearOff::window_button_release));
79         own_window.signal_motion_notify_event().connect (mem_fun (*this, &TearOff::window_motion));
80         own_window.signal_delete_event().connect (mem_fun (*this, &TearOff::window_delete_event));
81         own_window.signal_realize().connect (sigc::mem_fun (*this, &TearOff::own_window_realized));
82         own_window.signal_configure_event().connect (sigc::mem_fun (*this, &TearOff::own_window_configured), false);
83
84         tearoff_arrow.set_name ("TearOffArrow");
85         close_arrow.set_name ("TearOffArrow");
86
87         VBox* box2;
88         box2 = manage (new VBox);
89         box2->pack_start (tearoff_event_box, false, false);
90
91         pack_start (contents);
92         pack_start (*box2, false, false);
93 }
94
95 TearOff::~TearOff ()
96 {
97 }
98
99 void
100 TearOff::set_can_be_torn_off (bool yn)
101 {
102         if (yn != _can_be_torn_off) {
103                 if (yn) {
104                         tearoff_arrow.set_no_show_all (false);
105                         tearoff_arrow.show ();
106                 } else {
107                         tearoff_arrow.set_no_show_all (true);
108                         tearoff_arrow.hide ();
109                 }
110                 _can_be_torn_off = yn;
111         }
112 }
113
114 void
115 TearOff::set_visible (bool yn, bool force)
116 {
117         /* don't change visibility if torn off */
118
119         if (_torn) {
120                 return;
121         }
122
123         if (_visible != yn || force) {
124                 _visible = yn;
125                 if (yn) {
126                         show_all();
127                         Visible ();
128                 } else {
129                         hide ();
130                         Hidden ();
131                 }
132         }
133 }
134
135 gint
136 TearOff::tearoff_click (GdkEventButton* /*ev*/)
137 {
138         tear_it_off ();
139         return true;
140 }
141
142 void
143 TearOff::tear_it_off ()
144 {
145         if (!_can_be_torn_off) {
146                 return;
147         }
148
149         if (torn_off()) {
150                 return;
151         }
152
153         remove (contents);
154         window_box.pack_start (contents);
155         own_window.set_name (get_name());
156         close_event_box.set_name (get_name());
157         if (own_window_width == 0) {
158                 own_window.set_position (WIN_POS_MOUSE);
159         }
160         own_window.show_all ();
161         own_window.present ();
162         hide ();
163
164         _torn = true;
165
166         Detach ();
167 }
168
169 gint
170 TearOff::close_click (GdkEventButton* /*ev*/)
171 {
172         put_it_back ();
173         return true;
174 }
175
176 void
177 TearOff::put_it_back ()
178 {
179         if (!torn_off()) {
180                 return;
181         }
182
183         window_box.remove (contents);
184         pack_start (contents);
185         reorder_child (contents, 0);
186         own_window.hide ();
187         show_all ();
188
189         _torn = false;
190
191         Attach ();
192 }
193
194 gint
195 TearOff::window_button_press (GdkEventButton* ev)
196 {
197         if (dragging || ev->button != 1) {
198                 dragging = false;
199                 own_window.remove_modal_grab();
200                 return true;
201         }
202
203         dragging = true;
204         drag_x = ev->x_root;
205         drag_y = ev->y_root;
206
207         own_window.add_modal_grab();
208
209         return true;
210 }
211
212 gint
213 TearOff::window_button_release (GdkEventButton* /*ev*/)
214 {
215         dragging = false;
216         own_window.remove_modal_grab();
217         return true;
218 }
219
220 gint
221 TearOff::window_delete_event (GdkEventAny* /*ev*/)
222 {
223         return close_click(0);
224 }
225
226 gint
227 TearOff::window_motion (GdkEventMotion* ev)
228 {
229         gint x;
230         gint y;
231         gint mx, my;
232         double x_delta;
233         double y_delta;
234         RefPtr<Gdk::Window> win (own_window.get_window());
235
236         own_window.get_pointer (mx, my);
237
238         if (!dragging) {
239                 return true;
240         }
241
242         if (!(ev->state & GDK_BUTTON1_MASK)) {
243                 dragging = false;
244                 own_window.remove_modal_grab();
245                 return true;
246         }
247
248         x_delta = ev->x_root - drag_x;
249         y_delta = ev->y_root - drag_y;
250
251         win->get_root_origin (x, y);
252         win->move ((gint) floor (x + x_delta), (gint) floor (y + y_delta));
253
254         drag_x = ev->x_root;
255         drag_y = ev->y_root;
256
257         return true;
258 }
259
260 bool
261 TearOff::torn_off() const
262 {
263         return _torn;
264 }
265
266 void
267 TearOff::add_state (XMLNode& node) const
268 {
269         node.set_property ("tornoff", _torn);
270
271         if (own_window_width > 0) {
272                 node.set_property ("width", own_window_width);
273                 node.set_property ("height", own_window_height);
274                 node.set_property ("xpos", own_window_xpos);
275                 node.set_property ("ypos", own_window_ypos);
276         }
277 }
278
279 void
280 TearOff::set_state (const XMLNode& node)
281 {
282         Glib::RefPtr<Gdk::Window> win;
283
284         bool tornoff;
285         if (!node.get_property (X_("tornoff"), tornoff)) {
286                 return;
287         }
288
289         if (tornoff) {
290                 tear_it_off ();
291         } else {
292                 put_it_back ();
293         }
294
295         node.get_property (X_("width"), own_window_width);
296         node.get_property (X_("height"), own_window_height);
297         node.get_property (X_("xpos"), own_window_xpos);
298         node.get_property (X_("ypos"), own_window_ypos);
299
300         if (own_window.is_realized ()) {
301                 own_window.set_default_size (own_window_width, own_window_height);
302                 own_window.move (own_window_xpos, own_window_ypos);
303         }
304         /* otherwise do it once the window is realized, see below */
305 }
306
307 void
308 TearOff::own_window_realized ()
309 {
310         own_window.get_window()->set_decorations (WMDecoration (DECOR_BORDER|DECOR_RESIZEH));
311
312         if (own_window_width > 0) {
313                 own_window.set_default_size (own_window_width, own_window_height);
314                 own_window.move (own_window_xpos, own_window_ypos);
315         }
316 }
317
318 bool
319 TearOff::own_window_configured (GdkEventConfigure*)
320 {
321         Glib::RefPtr<const Gdk::Window> win;
322
323         win = own_window.get_window ();
324
325         if (win) {
326                 win->get_size (own_window_width, own_window_height);
327                 win->get_position (own_window_xpos, own_window_ypos);
328         }
329
330         return false;
331 }
332
333 void
334 TearOff::hide_visible ()
335 {
336         if (torn_off()) {
337                 own_window.hide ();
338         }
339
340         hide ();
341 }