enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[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 "pbd/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         , 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.add_property ("tornoff", (_torn ? "yes" : "no"));
270
271         if (own_window_width > 0) {
272                 char buf[32];
273
274                 snprintf (buf, sizeof (buf), "%d", own_window_width);
275                 node.add_property ("width", buf);
276                 snprintf (buf, sizeof (buf), "%d", own_window_height);
277                 node.add_property ("height", buf);
278                 snprintf (buf, sizeof (buf), "%d", own_window_xpos);
279                 node.add_property ("xpos", buf);
280                 snprintf (buf, sizeof (buf), "%d", own_window_ypos);
281                 node.add_property ("ypos", buf);
282         }
283 }
284
285 void
286 TearOff::set_state (const XMLNode& node)
287 {
288         Glib::RefPtr<Gdk::Window> win;
289         XMLProperty const * prop;
290
291         if ((prop = node.property (X_("tornoff"))) == 0) {
292                 return;
293         }
294
295         if (prop->value() == "yes") {
296                 tear_it_off ();
297         } else {
298                 put_it_back ();
299         }
300
301         if ((prop = node.property (X_("width"))) != 0) {
302                 sscanf (prop->value().c_str(), "%d", &own_window_width);
303         }
304         if ((prop = node.property (X_("height"))) != 0) {
305                 sscanf (prop->value().c_str(), "%d", &own_window_height);
306         }
307         if ((prop = node.property (X_("xpos"))) != 0) {
308                 sscanf (prop->value().c_str(), "%d", &own_window_xpos);
309         }
310         if ((prop = node.property (X_("ypos"))) != 0) {
311                 sscanf (prop->value().c_str(), "%d", &own_window_ypos);
312         }
313
314         if (own_window.is_realized()) {
315                 own_window.set_default_size (own_window_width, own_window_height);
316                 own_window.move (own_window_xpos, own_window_ypos);
317         }
318         /* otherwise do it once the window is realized, see below */
319 }
320
321 void
322 TearOff::own_window_realized ()
323 {
324         own_window.get_window()->set_decorations (WMDecoration (DECOR_BORDER|DECOR_RESIZEH));
325
326         if (own_window_width > 0) {
327                 own_window.set_default_size (own_window_width, own_window_height);
328                 own_window.move (own_window_xpos, own_window_ypos);
329         }
330 }
331
332 bool
333 TearOff::own_window_configured (GdkEventConfigure*)
334 {
335         Glib::RefPtr<const Gdk::Window> win;
336
337         win = own_window.get_window ();
338
339         if (win) {
340                 win->get_size (own_window_width, own_window_height);
341                 win->get_position (own_window_xpos, own_window_ypos);
342         }
343
344         return false;
345 }
346
347 void
348 TearOff::hide_visible ()
349 {
350         if (torn_off()) {
351                 own_window.hide ();
352         }
353
354         hide ();
355 }
356
357