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 <gtkmm2ext/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 (mem_fun(*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::on_size_allocate (Gtk::Allocation alloc)
129 width = alloc.get_width();
130 height = alloc.get_height();
132 DrawingArea::on_size_allocate (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(mem_fun(*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::on_motion_notify_event (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::on_expose_event (GdkEventExpose *event)
429 layout = create_pango_layout ("");
430 layout->set_font_description (get_style()->get_font());
433 /* redraw the background */
435 get_window()->draw_rectangle (get_style()->get_bg_gc(get_state()),
437 event->area.x, event->area.y,
438 event->area.width, event->area.height);
441 if (!panner.bypassed()) {
443 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
445 Target* puck = i->second;
450 fx = min (puck->x, 1.0f);
451 fx = max (fx, -1.0f);
452 x = (gint) floor (width * fx - 4);
454 fy = min (puck->y, 1.0f);
455 fy = max (fy, -1.0f);
456 y = (gint) floor (height * fy - 4);
458 get_window()->draw_arc (get_style()->get_fg_gc(Gtk::STATE_NORMAL),
464 layout->set_text (puck->text);
466 get_window()->draw_layout (get_style()->get_fg_gc (STATE_NORMAL), x+6, y+6, layout);
470 /* redraw any visible targets */
472 for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
473 Target *target = i->second;
475 if (target->visible) {
477 /* why -8 ??? why is this necessary ? */
479 fx = min (target->x, 1.0f);
480 fx = max (fx, -1.0f);
481 x = (gint) floor ((width - 8) * fx);
483 fy = min (target->y, 1.0f);
484 fy = max (fy, -1.0f);
485 y = (gint) floor ((height - 8) * fy);
487 get_window()->draw_rectangle (get_style()->get_fg_gc(Gtk::STATE_ACTIVE),
499 Panner2d::on_button_press_event (GdkEventButton *ev)
501 switch (ev->button) {
504 GdkModifierType state;
506 drag_target = find_closest_object (ev->x, ev->y, drag_index, drag_is_puck);
508 x = (int) floor (ev->x);
509 y = (int) floor (ev->y);
510 state = (GdkModifierType) ev->state;
512 return handle_motion (x, y, state);
522 Panner2d::on_button_release_event (GdkEventButton *ev)
524 switch (ev->button) {
528 GdkModifierType state;
530 x = (int) floor (ev->x);
531 y = (int) floor (ev->y);
532 state = (GdkModifierType) ev->state;
534 if (drag_is_puck && (Keyboard::modifier_state_contains (state, Keyboard::Shift))) {
536 for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
537 Target* puck = i->second;
547 ret = handle_motion (x, y, state);
559 show_context_menu ();
568 Panner2d::toggle_bypass ()
570 if (bypass_menu_item && (panner.bypassed() != bypass_menu_item->get_active())) {
571 panner.set_bypassed (!panner.bypassed());
576 Panner2d::show_context_menu ()
578 using namespace Menu_Helpers;
580 if (context_menu == 0) {
581 context_menu = manage (new Menu);
582 context_menu->set_name ("ArdourContextMenu");
583 MenuList& items = context_menu->items();
585 items.push_back (CheckMenuElem (_("Bypass")));
586 bypass_menu_item = static_cast<CheckMenuItem*> (&items.back());
587 bypass_menu_item->signal_toggled().connect (mem_fun(*this, &Panner2d::toggle_bypass));
591 bypass_menu_item->set_active (panner.bypassed());
592 context_menu->popup (1, 0);
596 Panner2d::allow_x_motion (bool yn)
602 Panner2d::allow_target_motion (bool yn)
608 Panner2d::allow_y_motion (bool yn)
614 Panner2d::puck_position (int which, float& x, float& y)
618 if ((i = pucks.find (which)) != pucks.end()) {
628 Panner2d::target_position (int which, float& x, float& y)
632 if ((i = targets.find (which)) != targets.end()) {