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.streampanner(drag_index).set_position (drag_target->x, drag_target->y);
415 panner.pan_control( drag_index )->set_value( drag_target->x );
416 //panner.control( Evoral::Parameter( PanAutomation, 1, drag_index ) )->set_value( drag_target->y );
420 TargetMoved (drag_index);
428 Panner2d::on_expose_event (GdkEventExpose *event)
434 layout = create_pango_layout ("");
435 layout->set_font_description (get_style()->get_font());
438 /* redraw the background */
440 get_window()->draw_rectangle (get_style()->get_bg_gc(get_state()),
442 event->area.x, event->area.y,
443 event->area.width, event->area.height);
446 if (!panner.bypassed()) {
448 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
450 Target* puck = i->second;
455 fx = min (puck->x, 1.0f);
456 fx = max (fx, -1.0f);
457 x = (gint) floor (width * fx - 4);
459 fy = min (puck->y, 1.0f);
460 fy = max (fy, -1.0f);
461 y = (gint) floor (height * fy - 4);
463 get_window()->draw_arc (get_style()->get_fg_gc(Gtk::STATE_NORMAL),
469 layout->set_text (puck->text);
471 get_window()->draw_layout (get_style()->get_fg_gc (STATE_NORMAL), x+6, y+6, layout);
475 /* redraw any visible targets */
477 for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
478 Target *target = i->second;
480 if (target->visible) {
482 /* why -8 ??? why is this necessary ? */
484 fx = min (target->x, 1.0f);
485 fx = max (fx, -1.0f);
486 x = (gint) floor ((width - 8) * fx);
488 fy = min (target->y, 1.0f);
489 fy = max (fy, -1.0f);
490 y = (gint) floor ((height - 8) * fy);
492 get_window()->draw_rectangle (get_style()->get_fg_gc(Gtk::STATE_ACTIVE),
504 Panner2d::on_button_press_event (GdkEventButton *ev)
506 switch (ev->button) {
509 GdkModifierType state;
511 drag_target = find_closest_object (ev->x, ev->y, drag_index, drag_is_puck);
513 x = (int) floor (ev->x);
514 y = (int) floor (ev->y);
515 state = (GdkModifierType) ev->state;
517 return handle_motion (x, y, state);
527 Panner2d::on_button_release_event (GdkEventButton *ev)
529 switch (ev->button) {
533 GdkModifierType state;
535 x = (int) floor (ev->x);
536 y = (int) floor (ev->y);
537 state = (GdkModifierType) ev->state;
539 if (drag_is_puck && (Keyboard::modifier_state_contains (state, Keyboard::TertiaryModifier))) {
541 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
542 Target* puck = i->second;
552 ret = handle_motion (x, y, state);
564 show_context_menu ();
573 Panner2d::toggle_bypass ()
575 if (bypass_menu_item && (panner.bypassed() != bypass_menu_item->get_active())) {
576 panner.set_bypassed (!panner.bypassed());
581 Panner2d::show_context_menu ()
583 using namespace Menu_Helpers;
585 if (context_menu == 0) {
586 context_menu = manage (new Menu);
587 context_menu->set_name ("ArdourContextMenu");
588 MenuList& items = context_menu->items();
590 items.push_back (CheckMenuElem (_("Bypass")));
591 bypass_menu_item = static_cast<CheckMenuItem*> (&items.back());
592 bypass_menu_item->signal_toggled().connect (mem_fun(*this, &Panner2d::toggle_bypass));
596 bypass_menu_item->set_active (panner.bypassed());
597 context_menu->popup (1, gtk_get_current_event_time());
601 Panner2d::allow_x_motion (bool yn)
607 Panner2d::allow_target_motion (bool yn)
613 Panner2d::allow_y_motion (bool yn)
619 Panner2d::puck_position (int which, float& x, float& y)
623 if ((i = pucks.find (which)) != pucks.end()) {
633 Panner2d::target_position (int which, float& x, float& y)
637 if ((i = targets.find (which)) != targets.end()) {