2 Copyright (C) 2002 Paul Davis
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.
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.
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.
24 #include <gtkmm/menu.h>
25 #include <gtkmm/checkmenuitem.h>
27 #include <pbd/error.h>
28 #include <ardour/panner.h>
29 #include <gtkmm2ext/gtk_ui.h>
33 #include "gui_thread.h"
40 using namespace ARDOUR
;
43 Panner2d::Target::Target (float xa
, float ya
, const char *txt
)
44 : x (xa
), y (ya
), text (txt
? strdup (txt
) : 0)
47 textlen
= strlen (txt
);
53 Panner2d::Target::~Target ()
60 Panner2d::Panner2d (Panner
& p
, int32_t h
)
61 : panner (p
), width (0), height (h
)
70 panner
.StateChanged
.connect (mem_fun(*this, &Panner2d::handle_state_change
));
73 set_events (Gdk::BUTTON_PRESS_MASK
|Gdk::BUTTON_RELEASE_MASK
|Gdk::POINTER_MOTION_MASK
);
79 for (Targets::iterator i
= targets
.begin(); i
!= targets
.end(); ++i
) {
85 Panner2d::reset (uint32_t n_inputs
)
96 add_puck ("", 0.0f
, 0.5f
);
100 add_puck ("L", 0.5f
, 0.25f
);
101 add_puck ("R", 0.25f
, 0.5f
);
107 for (uint32_t i
= 0; i
< n_inputs
; ++i
) {
109 snprintf (buf
, sizeof (buf
), "%" PRIu32
, i
);
110 add_puck (buf
, 0.0f
, 0.5f
);
116 /* add all outputs */
120 for (uint32_t n
= 0; n
< panner
.nouts(); ++n
) {
121 add_target (panner
.output (n
).x
, panner
.output (n
).y
);
124 allow_x_motion (true);
125 allow_y_motion (true);
126 allow_target_motion (true);
130 Panner2d::on_size_allocate (Gtk::Allocation
& alloc
)
132 width
= alloc
.get_width();
133 height
= alloc
.get_height();
135 DrawingArea::on_size_allocate (alloc
);
139 Panner2d::add_puck (const char* text
, float x
, float y
)
141 Target
* puck
= new Target (x
, y
, text
);
143 pair
<int,Target
*> newpair
;
144 newpair
.first
= pucks
.size();
145 newpair
.second
= puck
;
147 pucks
.insert (newpair
);
148 puck
->visible
= true;
154 Panner2d::add_target (float x
, float y
)
156 Target
*target
= new Target (x
, y
, "");
158 pair
<int,Target
*> newpair
;
159 newpair
.first
= targets
.size();
160 newpair
.second
= target
;
162 targets
.insert (newpair
);
163 target
->visible
= true;
166 return newpair
.first
;
170 Panner2d::drop_targets ()
172 for (Targets::iterator i
= targets
.begin(); i
!= targets
.end(); ) {
174 Targets::iterator tmp
;
189 Panner2d::drop_pucks ()
191 for (Targets::iterator i
= pucks
.begin(); i
!= pucks
.end(); ) {
193 Targets::iterator tmp
;
208 Panner2d::remove_target (int which
)
210 Targets::iterator i
= targets
.find (which
);
212 if (i
!= targets
.end()) {
220 Panner2d::handle_state_change ()
222 ENSURE_GUI_THREAD(mem_fun(*this, &Panner2d::handle_state_change
));
228 Panner2d::move_target (int which
, float x
, float y
)
230 Targets::iterator i
= targets
.find (which
);
237 if (i
!= targets
.end()) {
247 Panner2d::move_puck (int which
, float x
, float y
)
249 Targets::iterator i
= pucks
.find (which
);
252 if (i
!= pucks
.end()) {
262 Panner2d::show_puck (int which
)
264 Targets::iterator i
= pucks
.find (which
);
266 if (i
!= pucks
.end()) {
267 Target
* puck
= i
->second
;
268 if (!puck
->visible
) {
269 puck
->visible
= true;
276 Panner2d::hide_puck (int which
)
278 Targets::iterator i
= pucks
.find (which
);
280 if (i
!= pucks
.end()) {
281 Target
* puck
= i
->second
;
282 if (!puck
->visible
) {
283 puck
->visible
= false;
290 Panner2d::show_target (int which
)
292 Targets::iterator i
= targets
.find (which
);
293 if (i
!= targets
.end()) {
294 if (!i
->second
->visible
) {
295 i
->second
->visible
= true;
302 Panner2d::hide_target (int which
)
304 Targets::iterator i
= targets
.find (which
);
305 if (i
!= targets
.end()) {
306 if (i
->second
->visible
) {
307 i
->second
->visible
= false;
314 Panner2d::find_closest_object (gdouble x
, gdouble y
, int& which
, bool& is_puck
) const
320 float best_distance
= FLT_MAX
;
329 for (Targets::const_iterator i
= targets
.begin(); i
!= targets
.end(); ++i
, ++which
) {
330 candidate
= i
->second
;
332 distance
= sqrt ((candidate
->x
- efx
) * (candidate
->x
- efx
) +
333 (candidate
->y
- efy
) * (candidate
->y
- efy
));
335 if (distance
< best_distance
) {
337 best_distance
= distance
;
341 for (Targets::const_iterator i
= pucks
.begin(); i
!= pucks
.end(); ++i
, ++pwhich
) {
342 candidate
= i
->second
;
344 distance
= sqrt ((candidate
->x
- efx
) * (candidate
->x
- efx
) +
345 (candidate
->y
- efy
) * (candidate
->y
- efy
));
347 if (distance
< best_distance
) {
349 best_distance
= distance
;
359 Panner2d::on_motion_notify_event (GdkEventMotion
*ev
)
362 GdkModifierType state
;
365 gdk_window_get_pointer (ev
->window
, &x
, &y
, &state
);
367 x
= (int) floor (ev
->x
);
368 y
= (int) floor (ev
->y
);
369 state
= (GdkModifierType
) ev
->state
;
371 return handle_motion (x
, y
, state
);
374 Panner2d::handle_motion (gint evx
, gint evy
, GdkModifierType state
)
376 if (drag_target
== 0 || (state
& GDK_BUTTON1_MASK
) == 0) {
381 bool need_move
= false;
383 if (!drag_is_puck
&& !allow_target
) {
387 if (allow_x
|| !drag_is_puck
) {
389 x
= min (evx
, width
- 1);
391 new_x
= (float) x
/ (width
- 1);
392 if (new_x
!= drag_target
->x
) {
393 drag_target
->x
= new_x
;
398 if (allow_y
|| drag_is_puck
) {
400 y
= min (evy
, height
- 1);
402 new_y
= (float) y
/ (height
- 1);
403 if (new_y
!= drag_target
->y
) {
404 drag_target
->y
= new_y
;
414 panner
[drag_index
]->set_position (drag_target
->x
, drag_target
->y
);
418 TargetMoved (drag_index
);
426 Panner2d::on_expose_event (GdkEventExpose
*event
)
432 layout
= create_pango_layout ("");
433 layout
->set_font_description (get_style()->get_font());
436 /* redraw the background */
438 get_window()->draw_rectangle (get_style()->get_bg_gc(get_state()),
440 event
->area
.x
, event
->area
.y
,
441 event
->area
.width
, event
->area
.height
);
444 if (!panner
.bypassed()) {
446 for (Targets::iterator i
= pucks
.begin(); i
!= pucks
.end(); ++i
) {
448 Target
* puck
= i
->second
;
453 fx
= min (puck
->x
, 1.0f
);
454 fx
= max (fx
, -1.0f
);
455 x
= (gint
) floor (width
* fx
- 4);
457 fy
= min (puck
->y
, 1.0f
);
458 fy
= max (fy
, -1.0f
);
459 y
= (gint
) floor (height
* fy
- 4);
461 get_window()->draw_arc (get_style()->get_fg_gc(Gtk::STATE_NORMAL
),
467 layout
->set_text (puck
->text
);
469 get_window()->draw_layout (get_style()->get_fg_gc (STATE_NORMAL
), x
+6, y
+6, layout
);
473 /* redraw any visible targets */
475 for (Targets::iterator i
= targets
.begin(); i
!= targets
.end(); ++i
) {
476 Target
*target
= i
->second
;
478 if (target
->visible
) {
480 /* why -8 ??? why is this necessary ? */
482 fx
= min (target
->x
, 1.0f
);
483 fx
= max (fx
, -1.0f
);
484 x
= (gint
) floor ((width
- 8) * fx
);
486 fy
= min (target
->y
, 1.0f
);
487 fy
= max (fy
, -1.0f
);
488 y
= (gint
) floor ((height
- 8) * fy
);
490 get_window()->draw_rectangle (get_style()->get_fg_gc(Gtk::STATE_ACTIVE
),
502 Panner2d::on_button_press_event (GdkEventButton
*ev
)
504 switch (ev
->button
) {
507 GdkModifierType state
;
509 drag_target
= find_closest_object (ev
->x
, ev
->y
, drag_index
, drag_is_puck
);
511 x
= (int) floor (ev
->x
);
512 y
= (int) floor (ev
->y
);
513 state
= (GdkModifierType
) ev
->state
;
515 return handle_motion (x
, y
, state
);
525 Panner2d::on_button_release_event (GdkEventButton
*ev
)
527 switch (ev
->button
) {
531 GdkModifierType state
;
533 x
= (int) floor (ev
->x
);
534 y
= (int) floor (ev
->y
);
535 state
= (GdkModifierType
) ev
->state
;
537 if (drag_is_puck
&& (Keyboard::modifier_state_contains (state
, Keyboard::TertiaryModifier
))) {
539 for (Targets::iterator i
= pucks
.begin(); i
!= pucks
.end(); ++i
) {
540 Target
* puck
= i
->second
;
550 ret
= handle_motion (x
, y
, state
);
562 show_context_menu ();
571 Panner2d::toggle_bypass ()
573 if (bypass_menu_item
&& (panner
.bypassed() != bypass_menu_item
->get_active())) {
574 panner
.set_bypassed (!panner
.bypassed());
579 Panner2d::show_context_menu ()
581 using namespace Menu_Helpers
;
583 if (context_menu
== 0) {
584 context_menu
= manage (new Menu
);
585 context_menu
->set_name ("ArdourContextMenu");
586 MenuList
& items
= context_menu
->items();
588 items
.push_back (CheckMenuElem (_("Bypass")));
589 bypass_menu_item
= static_cast<CheckMenuItem
*> (&items
.back());
590 bypass_menu_item
->signal_toggled().connect (mem_fun(*this, &Panner2d::toggle_bypass
));
594 bypass_menu_item
->set_active (panner
.bypassed());
595 context_menu
->popup (1, gtk_get_current_event_time());
599 Panner2d::allow_x_motion (bool yn
)
605 Panner2d::allow_target_motion (bool yn
)
611 Panner2d::allow_y_motion (bool yn
)
617 Panner2d::puck_position (int which
, float& x
, float& y
)
621 if ((i
= pucks
.find (which
)) != pucks
.end()) {
631 Panner2d::target_position (int which
, float& x
, float& y
)
635 if ((i
= targets
.find (which
)) != targets
.end()) {