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.
25 #include <pbd/error.h>
26 #include <ardour/panner.h>
27 #include <gtkmmext/gtk_ui.h>
31 #include "gui_thread.h"
38 using namespace ARDOUR;
40 Panner2d::Target::Target (float xa, float ya, const char *txt)
41 : x (xa), y (ya), text (txt ? strdup (txt) : 0)
44 textlen = strlen (txt);
50 Panner2d::Target::~Target ()
57 Panner2d::Panner2d (Panner& p, int32_t w, int32_t h)
58 : panner (p), width (w), height (h)
67 panner.StateChanged.connect (slot (*this, &Panner2d::handle_state_change));
70 set_events (GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_POINTER_MOTION_MASK);
76 for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
82 Panner2d::reset (uint32_t n_inputs)
93 add_puck ("", 0.0f, 0.5f);
97 add_puck ("L", 0.5f, 0.25f);
98 add_puck ("R", 0.25f, 0.5f);
104 for (uint32_t i = 0; i < n_inputs; ++i) {
106 snprintf (buf, sizeof (buf), "%" PRIu32, i);
107 add_puck (buf, 0.0f, 0.5f);
113 /* add all outputs */
117 for (uint32_t n = 0; n < panner.nouts(); ++n) {
118 add_target (panner.output (n).x, panner.output (n).y);
121 allow_x_motion (true);
122 allow_y_motion (true);
123 allow_target_motion (true);
127 Panner2d::size_allocate_impl (GtkAllocation *alloc)
129 width = alloc->width;
130 height = alloc->height;
132 DrawingArea::size_allocate_impl (alloc);
136 Panner2d::add_puck (const char* text, float x, float y)
138 Target* puck = new Target (x, y, text);
140 pair<int,Target *> newpair;
141 newpair.first = pucks.size();
142 newpair.second = puck;
144 pucks.insert (newpair);
145 puck->visible = true;
151 Panner2d::add_target (float x, float y)
153 Target *target = new Target (x, y, "");
155 pair<int,Target *> newpair;
156 newpair.first = targets.size();
157 newpair.second = target;
159 targets.insert (newpair);
160 target->visible = true;
163 return newpair.first;
167 Panner2d::drop_targets ()
169 for (Targets::iterator i = targets.begin(); i != targets.end(); ) {
171 Targets::iterator tmp;
186 Panner2d::drop_pucks ()
188 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ) {
190 Targets::iterator tmp;
205 Panner2d::remove_target (int which)
207 Targets::iterator i = targets.find (which);
209 if (i != targets.end()) {
217 Panner2d::handle_state_change ()
219 ENSURE_GUI_THREAD(slot (*this, &Panner2d::handle_state_change));
225 Panner2d::move_target (int which, float x, float y)
227 Targets::iterator i = targets.find (which);
234 if (i != targets.end()) {
244 Panner2d::move_puck (int which, float x, float y)
246 Targets::iterator i = pucks.find (which);
249 if (i != pucks.end()) {
259 Panner2d::show_puck (int which)
261 Targets::iterator i = pucks.find (which);
263 if (i != pucks.end()) {
264 Target* puck = i->second;
265 if (!puck->visible) {
266 puck->visible = true;
273 Panner2d::hide_puck (int which)
275 Targets::iterator i = pucks.find (which);
277 if (i != pucks.end()) {
278 Target* puck = i->second;
279 if (!puck->visible) {
280 puck->visible = false;
287 Panner2d::show_target (int which)
289 Targets::iterator i = targets.find (which);
290 if (i != targets.end()) {
291 if (!i->second->visible) {
292 i->second->visible = true;
299 Panner2d::hide_target (int which)
301 Targets::iterator i = targets.find (which);
302 if (i != targets.end()) {
303 if (i->second->visible) {
304 i->second->visible = false;
311 Panner2d::find_closest_object (gdouble x, gdouble y, int& which, bool& is_puck) const
317 float best_distance = FLT_MAX;
326 for (Targets::const_iterator i = targets.begin(); i != targets.end(); ++i, ++which) {
327 candidate = i->second;
329 distance = sqrt ((candidate->x - efx) * (candidate->x - efx) +
330 (candidate->y - efy) * (candidate->y - efy));
332 if (distance < best_distance) {
334 best_distance = distance;
338 for (Targets::const_iterator i = pucks.begin(); i != pucks.end(); ++i, ++pwhich) {
339 candidate = i->second;
341 distance = sqrt ((candidate->x - efx) * (candidate->x - efx) +
342 (candidate->y - efy) * (candidate->y - efy));
344 if (distance < best_distance) {
346 best_distance = distance;
356 Panner2d::motion_notify_event_impl (GdkEventMotion *ev)
359 GdkModifierType state;
362 gdk_window_get_pointer (ev->window, &x, &y, &state);
364 x = (int) floor (ev->x);
365 y = (int) floor (ev->y);
366 state = (GdkModifierType) ev->state;
368 return handle_motion (x, y, state);
371 Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state)
373 if (drag_target == 0 || (state & GDK_BUTTON1_MASK) == 0) {
378 bool need_move = false;
380 if (!drag_is_puck && !allow_target) {
384 if (allow_x || !drag_is_puck) {
386 x = min (evx, width - 1);
388 new_x = (float) x / (width - 1);
389 if (new_x != drag_target->x) {
390 drag_target->x = new_x;
395 if (allow_y || drag_is_puck) {
397 y = min (evy, height - 1);
399 new_y = (float) y / (height - 1);
400 if (new_y != drag_target->y) {
401 drag_target->y = new_y;
411 panner[drag_index]->set_position (drag_target->x, drag_target->y);
415 TargetMoved (drag_index);
423 Panner2d::expose_event_impl (GdkEventExpose *event)
428 /* redraw the background */
430 get_window().draw_rectangle (get_style()->get_bg_gc(get_state()),
432 event->area.x, event->area.y,
433 event->area.width, event->area.height);
436 if (!panner.bypassed()) {
438 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
440 Target* puck = i->second;
445 fx = min (puck->x, 1.0f);
446 fx = max (fx, -1.0f);
447 x = (gint) floor (width * fx - 4);
449 fy = min (puck->y, 1.0f);
450 fy = max (fy, -1.0f);
451 y = (gint) floor (height * fy - 4);
453 get_window().draw_arc (get_style()->get_fg_gc(GTK_STATE_NORMAL),
458 get_window().draw_text (get_style()->get_font(),
459 get_style()->get_fg_gc(GTK_STATE_NORMAL),
466 /* redraw any visible targets */
468 for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
469 Target *target = i->second;
471 if (target->visible) {
473 /* why -8 ??? why is this necessary ? */
475 fx = min (target->x, 1.0f);
476 fx = max (fx, -1.0f);
477 x = (gint) floor ((width - 8) * fx);
479 fy = min (target->y, 1.0f);
480 fy = max (fy, -1.0f);
481 y = (gint) floor ((height - 8) * fy);
483 get_window().draw_rectangle (get_style()->get_fg_gc(GTK_STATE_ACTIVE),
495 Panner2d::button_press_event_impl (GdkEventButton *ev)
497 switch (ev->button) {
500 GdkModifierType state;
502 drag_target = find_closest_object (ev->x, ev->y, drag_index, drag_is_puck);
504 x = (int) floor (ev->x);
505 y = (int) floor (ev->y);
506 state = (GdkModifierType) ev->state;
508 return handle_motion (x, y, state);
518 Panner2d::button_release_event_impl (GdkEventButton *ev)
520 switch (ev->button) {
524 GdkModifierType state;
526 x = (int) floor (ev->x);
527 y = (int) floor (ev->y);
528 state = (GdkModifierType) ev->state;
530 if (drag_is_puck && (Keyboard::modifier_state_contains (state, Keyboard::Shift))) {
532 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
533 Target* puck = i->second;
543 ret = handle_motion (x, y, state);
555 show_context_menu ();
564 Panner2d::toggle_bypass ()
566 if (bypass_menu_item && (panner.bypassed() != bypass_menu_item->get_active())) {
567 panner.set_bypassed (!panner.bypassed());
572 Panner2d::show_context_menu ()
574 using namespace Menu_Helpers;
576 if (context_menu == 0) {
577 context_menu = manage (new Menu);
578 context_menu->set_name ("ArdourContextMenu");
579 MenuList& items = context_menu->items();
581 items.push_back (CheckMenuElem (_("Bypass")));
582 bypass_menu_item = static_cast<CheckMenuItem*> (items.back());
583 bypass_menu_item->toggled.connect (slot (*this, &Panner2d::toggle_bypass));
587 bypass_menu_item->set_active (panner.bypassed());
588 context_menu->popup (1, 0);
592 Panner2d::allow_x_motion (bool yn)
598 Panner2d::allow_target_motion (bool yn)
604 Panner2d::allow_y_motion (bool yn)
610 Panner2d::puck_position (int which, float& x, float& y)
614 if ((i = pucks.find (which)) != pucks.end()) {
624 Panner2d::target_position (int which, float& x, float& y)
628 if ((i = targets.find (which)) != targets.end()) {