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 <gtkmm/menu.h>
26 #include <gtkmm/checkmenuitem.h>
28 #include <pbd/error.h>
29 #include <ardour/panner.h>
30 #include <gtkmm2ext/gtk_ui.h>
34 #include "gui_thread.h"
41 using namespace ARDOUR;
44 Panner2d::Target::Target (float xa, float ya, const char *txt)
45 : x (xa), y (ya), text (txt ? strdup (txt) : 0)
48 textlen = strlen (txt);
54 Panner2d::Target::~Target ()
61 Panner2d::Panner2d (Panner& p, int32_t w, int32_t h)
62 : panner (p), width (w), height (h)
71 panner.StateChanged.connect (mem_fun(*this, &Panner2d::handle_state_change));
74 set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
80 for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
86 Panner2d::reset (uint32_t n_inputs)
97 add_puck ("", 0.0f, 0.5f);
101 add_puck ("L", 0.5f, 0.25f);
102 add_puck ("R", 0.25f, 0.5f);
108 for (uint32_t i = 0; i < n_inputs; ++i) {
110 snprintf (buf, sizeof (buf), "%" PRIu32, i);
111 add_puck (buf, 0.0f, 0.5f);
117 /* add all outputs */
121 for (uint32_t n = 0; n < panner.nouts(); ++n) {
122 add_target (panner.output (n).x, panner.output (n).y);
125 allow_x_motion (true);
126 allow_y_motion (true);
127 allow_target_motion (true);
131 Panner2d::on_size_allocate (Gtk::Allocation& alloc)
133 width = alloc.get_width();
134 height = alloc.get_height();
136 DrawingArea::on_size_allocate (alloc);
140 Panner2d::add_puck (const char* text, float x, float y)
142 Target* puck = new Target (x, y, text);
144 pair<int,Target *> newpair;
145 newpair.first = pucks.size();
146 newpair.second = puck;
148 pucks.insert (newpair);
149 puck->visible = true;
155 Panner2d::add_target (float x, float y)
157 Target *target = new Target (x, y, "");
159 pair<int,Target *> newpair;
160 newpair.first = targets.size();
161 newpair.second = target;
163 targets.insert (newpair);
164 target->visible = true;
167 return newpair.first;
171 Panner2d::drop_targets ()
173 for (Targets::iterator i = targets.begin(); i != targets.end(); ) {
175 Targets::iterator tmp;
190 Panner2d::drop_pucks ()
192 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ) {
194 Targets::iterator tmp;
209 Panner2d::remove_target (int which)
211 Targets::iterator i = targets.find (which);
213 if (i != targets.end()) {
221 Panner2d::handle_state_change ()
223 ENSURE_GUI_THREAD(mem_fun(*this, &Panner2d::handle_state_change));
229 Panner2d::move_target (int which, float x, float y)
231 Targets::iterator i = targets.find (which);
238 if (i != targets.end()) {
248 Panner2d::move_puck (int which, float x, float y)
250 Targets::iterator i = pucks.find (which);
253 if (i != pucks.end()) {
263 Panner2d::show_puck (int which)
265 Targets::iterator i = pucks.find (which);
267 if (i != pucks.end()) {
268 Target* puck = i->second;
269 if (!puck->visible) {
270 puck->visible = true;
277 Panner2d::hide_puck (int which)
279 Targets::iterator i = pucks.find (which);
281 if (i != pucks.end()) {
282 Target* puck = i->second;
283 if (!puck->visible) {
284 puck->visible = false;
291 Panner2d::show_target (int which)
293 Targets::iterator i = targets.find (which);
294 if (i != targets.end()) {
295 if (!i->second->visible) {
296 i->second->visible = true;
303 Panner2d::hide_target (int which)
305 Targets::iterator i = targets.find (which);
306 if (i != targets.end()) {
307 if (i->second->visible) {
308 i->second->visible = false;
315 Panner2d::find_closest_object (gdouble x, gdouble y, int& which, bool& is_puck) const
321 float best_distance = FLT_MAX;
330 for (Targets::const_iterator i = targets.begin(); i != targets.end(); ++i, ++which) {
331 candidate = i->second;
333 distance = sqrt ((candidate->x - efx) * (candidate->x - efx) +
334 (candidate->y - efy) * (candidate->y - efy));
336 if (distance < best_distance) {
338 best_distance = distance;
342 for (Targets::const_iterator i = pucks.begin(); i != pucks.end(); ++i, ++pwhich) {
343 candidate = i->second;
345 distance = sqrt ((candidate->x - efx) * (candidate->x - efx) +
346 (candidate->y - efy) * (candidate->y - efy));
348 if (distance < best_distance) {
350 best_distance = distance;
360 Panner2d::on_motion_notify_event (GdkEventMotion *ev)
363 GdkModifierType state;
366 gdk_window_get_pointer (ev->window, &x, &y, &state);
368 x = (int) floor (ev->x);
369 y = (int) floor (ev->y);
370 state = (GdkModifierType) ev->state;
372 return handle_motion (x, y, state);
375 Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state)
377 if (drag_target == 0 || (state & GDK_BUTTON1_MASK) == 0) {
382 bool need_move = false;
384 if (!drag_is_puck && !allow_target) {
388 if (allow_x || !drag_is_puck) {
390 x = min (evx, width - 1);
392 new_x = (float) x / (width - 1);
393 if (new_x != drag_target->x) {
394 drag_target->x = new_x;
399 if (allow_y || drag_is_puck) {
401 y = min (evy, height - 1);
403 new_y = (float) y / (height - 1);
404 if (new_y != drag_target->y) {
405 drag_target->y = new_y;
415 panner[drag_index]->set_position (drag_target->x, drag_target->y);
419 TargetMoved (drag_index);
427 Panner2d::on_expose_event (GdkEventExpose *event)
433 layout = create_pango_layout ("");
434 layout->set_font_description (get_style()->get_font());
437 /* redraw the background */
439 get_window()->draw_rectangle (get_style()->get_bg_gc(get_state()),
441 event->area.x, event->area.y,
442 event->area.width, event->area.height);
445 if (!panner.bypassed()) {
447 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
449 Target* puck = i->second;
454 fx = min (puck->x, 1.0f);
455 fx = max (fx, -1.0f);
456 x = (gint) floor (width * fx - 4);
458 fy = min (puck->y, 1.0f);
459 fy = max (fy, -1.0f);
460 y = (gint) floor (height * fy - 4);
462 get_window()->draw_arc (get_style()->get_fg_gc(Gtk::STATE_NORMAL),
468 layout->set_text (puck->text);
470 get_window()->draw_layout (get_style()->get_fg_gc (STATE_NORMAL), x+6, y+6, layout);
474 /* redraw any visible targets */
476 for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
477 Target *target = i->second;
479 if (target->visible) {
481 /* why -8 ??? why is this necessary ? */
483 fx = min (target->x, 1.0f);
484 fx = max (fx, -1.0f);
485 x = (gint) floor ((width - 8) * fx);
487 fy = min (target->y, 1.0f);
488 fy = max (fy, -1.0f);
489 y = (gint) floor ((height - 8) * fy);
491 get_window()->draw_rectangle (get_style()->get_fg_gc(Gtk::STATE_ACTIVE),
503 Panner2d::on_button_press_event (GdkEventButton *ev)
505 switch (ev->button) {
508 GdkModifierType state;
510 drag_target = find_closest_object (ev->x, ev->y, drag_index, drag_is_puck);
512 x = (int) floor (ev->x);
513 y = (int) floor (ev->y);
514 state = (GdkModifierType) ev->state;
516 return handle_motion (x, y, state);
526 Panner2d::on_button_release_event (GdkEventButton *ev)
528 switch (ev->button) {
532 GdkModifierType state;
534 x = (int) floor (ev->x);
535 y = (int) floor (ev->y);
536 state = (GdkModifierType) ev->state;
538 if (drag_is_puck && (Keyboard::modifier_state_contains (state, Keyboard::Shift))) {
540 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
541 Target* puck = i->second;
551 ret = handle_motion (x, y, state);
563 show_context_menu ();
572 Panner2d::toggle_bypass ()
574 if (bypass_menu_item && (panner.bypassed() != bypass_menu_item->get_active())) {
575 panner.set_bypassed (!panner.bypassed());
580 Panner2d::show_context_menu ()
582 using namespace Menu_Helpers;
584 if (context_menu == 0) {
585 context_menu = manage (new Menu);
586 context_menu->set_name ("ArdourContextMenu");
587 MenuList& items = context_menu->items();
589 items.push_back (CheckMenuElem (_("Bypass")));
590 bypass_menu_item = static_cast<CheckMenuItem*> (&items.back());
591 bypass_menu_item->signal_toggled().connect (mem_fun(*this, &Panner2d::toggle_bypass));
595 bypass_menu_item->set_active (panner.bypassed());
596 context_menu->popup (1, gtk_get_current_event_time());
600 Panner2d::allow_x_motion (bool yn)
606 Panner2d::allow_target_motion (bool yn)
612 Panner2d::allow_y_motion (bool yn)
618 Panner2d::puck_position (int which, float& x, float& y)
622 if ((i = pucks.find (which)) != pucks.end()) {
632 Panner2d::target_position (int which, float& x, float& y)
636 if ((i = targets.find (which)) != targets.end()) {