summaryrefslogtreecommitdiff
path: root/gtk2_ardour/panner2d.cc
diff options
context:
space:
mode:
authorTaybin Rutkin <taybin@taybin.com>2005-09-25 18:42:24 +0000
committerTaybin Rutkin <taybin@taybin.com>2005-09-25 18:42:24 +0000
commit209d967b1bb80a9735d690d8f4f0455ecb9970ca (patch)
tree9d76ddcd7c1ac9d91bb2b1a33d31b66ce4ded5de /gtk2_ardour/panner2d.cc
parente4b9aed743fc765219ac775905a221c017c88fba (diff)
Initial import of gtk2_ardour.
git-svn-id: svn://localhost/trunk/ardour2@24 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'gtk2_ardour/panner2d.cc')
-rw-r--r--gtk2_ardour/panner2d.cc636
1 files changed, 636 insertions, 0 deletions
diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc
new file mode 100644
index 0000000000..ceec33868b
--- /dev/null
+++ b/gtk2_ardour/panner2d.cc
@@ -0,0 +1,636 @@
+/*
+ Copyright (C) 2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <cmath>
+#include <climits>
+#include <string.h>
+
+#include <pbd/error.h>
+#include <ardour/panner.h>
+#include <gtkmmext/gtk_ui.h>
+
+#include "panner2d.h"
+#include "keyboard.h"
+#include "gui_thread.h"
+
+#include "i18n.h"
+
+using namespace std;
+using namespace Gtk;
+using namespace SigC;
+using namespace ARDOUR;
+
+Panner2d::Target::Target (float xa, float ya, const char *txt)
+ : x (xa), y (ya), text (txt ? strdup (txt) : 0)
+{
+ if (text) {
+ textlen = strlen (txt);
+ } else {
+ textlen = 0;
+ }
+}
+
+Panner2d::Target::~Target ()
+{
+ if (text) {
+ free (text);
+ }
+}
+
+Panner2d::Panner2d (Panner& p, int32_t w, int32_t h)
+ : panner (p), width (w), height (h)
+{
+ context_menu = 0;
+ bypass_menu_item = 0;
+
+ allow_x = false;
+ allow_y = false;
+ allow_target = false;
+
+ panner.StateChanged.connect (slot (*this, &Panner2d::handle_state_change));
+
+ drag_target = 0;
+ set_events (GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_POINTER_MOTION_MASK);
+
+}
+
+Panner2d::~Panner2d()
+{
+ for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
+ delete i->second;
+ }
+}
+
+void
+Panner2d::reset (uint32_t n_inputs)
+{
+ /* add pucks */
+
+ drop_pucks ();
+
+ switch (n_inputs) {
+ case 0:
+ break;
+
+ case 1:
+ add_puck ("", 0.0f, 0.5f);
+ break;
+
+ case 2:
+ add_puck ("L", 0.5f, 0.25f);
+ add_puck ("R", 0.25f, 0.5f);
+ show_puck (0);
+ show_puck (1);
+ break;
+
+ default:
+ for (uint32_t i = 0; i < n_inputs; ++i) {
+ char buf[64];
+ snprintf (buf, sizeof (buf), "%" PRIu32, i);
+ add_puck (buf, 0.0f, 0.5f);
+ show_puck (i);
+ }
+ break;
+ }
+
+ /* add all outputs */
+
+ drop_targets ();
+
+ for (uint32_t n = 0; n < panner.nouts(); ++n) {
+ add_target (panner.output (n).x, panner.output (n).y);
+ }
+
+ allow_x_motion (true);
+ allow_y_motion (true);
+ allow_target_motion (true);
+}
+
+void
+Panner2d::size_allocate_impl (GtkAllocation *alloc)
+{
+ width = alloc->width;
+ height = alloc->height;
+
+ DrawingArea::size_allocate_impl (alloc);
+}
+
+int
+Panner2d::add_puck (const char* text, float x, float y)
+{
+ Target* puck = new Target (x, y, text);
+
+ pair<int,Target *> newpair;
+ newpair.first = pucks.size();
+ newpair.second = puck;
+
+ pucks.insert (newpair);
+ puck->visible = true;
+
+ return 0;
+}
+
+int
+Panner2d::add_target (float x, float y)
+{
+ Target *target = new Target (x, y, "");
+
+ pair<int,Target *> newpair;
+ newpair.first = targets.size();
+ newpair.second = target;
+
+ targets.insert (newpair);
+ target->visible = true;
+ queue_draw ();
+
+ return newpair.first;
+}
+
+void
+Panner2d::drop_targets ()
+{
+ for (Targets::iterator i = targets.begin(); i != targets.end(); ) {
+
+ Targets::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ delete i->second;
+ targets.erase (i);
+
+ i = tmp;
+ }
+
+ queue_draw ();
+}
+
+void
+Panner2d::drop_pucks ()
+{
+ for (Targets::iterator i = pucks.begin(); i != pucks.end(); ) {
+
+ Targets::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ delete i->second;
+ pucks.erase (i);
+
+ i = tmp;
+ }
+
+ queue_draw ();
+}
+
+void
+Panner2d::remove_target (int which)
+{
+ Targets::iterator i = targets.find (which);
+
+ if (i != targets.end()) {
+ delete i->second;
+ targets.erase (i);
+ queue_draw ();
+ }
+}
+
+void
+Panner2d::handle_state_change ()
+{
+ ENSURE_GUI_THREAD(slot (*this, &Panner2d::handle_state_change));
+
+ queue_draw ();
+}
+
+void
+Panner2d::move_target (int which, float x, float y)
+{
+ Targets::iterator i = targets.find (which);
+ Target *target;
+
+ if (!allow_target) {
+ return;
+ }
+
+ if (i != targets.end()) {
+ target = i->second;
+ target->x = x;
+ target->y = y;
+
+ queue_draw ();
+ }
+}
+
+void
+Panner2d::move_puck (int which, float x, float y)
+{
+ Targets::iterator i = pucks.find (which);
+ Target *target;
+
+ if (i != pucks.end()) {
+ target = i->second;
+ target->x = x;
+ target->y = y;
+
+ queue_draw ();
+ }
+}
+
+void
+Panner2d::show_puck (int which)
+{
+ Targets::iterator i = pucks.find (which);
+
+ if (i != pucks.end()) {
+ Target* puck = i->second;
+ if (!puck->visible) {
+ puck->visible = true;
+ queue_draw ();
+ }
+ }
+}
+
+void
+Panner2d::hide_puck (int which)
+{
+ Targets::iterator i = pucks.find (which);
+
+ if (i != pucks.end()) {
+ Target* puck = i->second;
+ if (!puck->visible) {
+ puck->visible = false;
+ queue_draw ();
+ }
+ }
+}
+
+void
+Panner2d::show_target (int which)
+{
+ Targets::iterator i = targets.find (which);
+ if (i != targets.end()) {
+ if (!i->second->visible) {
+ i->second->visible = true;
+ queue_draw ();
+ }
+ }
+}
+
+void
+Panner2d::hide_target (int which)
+{
+ Targets::iterator i = targets.find (which);
+ if (i != targets.end()) {
+ if (i->second->visible) {
+ i->second->visible = false;
+ queue_draw ();
+ }
+ }
+}
+
+Panner2d::Target *
+Panner2d::find_closest_object (gdouble x, gdouble y, int& which, bool& is_puck) const
+{
+ gdouble efx, efy;
+ Target *closest = 0;
+ Target *candidate;
+ float distance;
+ float best_distance = FLT_MAX;
+ int pwhich;
+
+ efx = x/width;
+ efy = y/height;
+ which = 0;
+ pwhich = 0;
+ is_puck = false;
+
+ for (Targets::const_iterator i = targets.begin(); i != targets.end(); ++i, ++which) {
+ candidate = i->second;
+
+ distance = sqrt ((candidate->x - efx) * (candidate->x - efx) +
+ (candidate->y - efy) * (candidate->y - efy));
+
+ if (distance < best_distance) {
+ closest = candidate;
+ best_distance = distance;
+ }
+ }
+
+ for (Targets::const_iterator i = pucks.begin(); i != pucks.end(); ++i, ++pwhich) {
+ candidate = i->second;
+
+ distance = sqrt ((candidate->x - efx) * (candidate->x - efx) +
+ (candidate->y - efy) * (candidate->y - efy));
+
+ if (distance < best_distance) {
+ closest = candidate;
+ best_distance = distance;
+ is_puck = true;
+ which = pwhich;
+ }
+ }
+
+ return closest;
+}
+
+gint
+Panner2d::motion_notify_event_impl (GdkEventMotion *ev)
+{
+ gint x, y;
+ GdkModifierType state;
+
+ if (ev->is_hint) {
+ gdk_window_get_pointer (ev->window, &x, &y, &state);
+ } else {
+ x = (int) floor (ev->x);
+ y = (int) floor (ev->y);
+ state = (GdkModifierType) ev->state;
+ }
+ return handle_motion (x, y, state);
+}
+gint
+Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state)
+{
+ if (drag_target == 0 || (state & GDK_BUTTON1_MASK) == 0) {
+ return FALSE;
+ }
+
+ int x, y;
+ bool need_move = false;
+
+ if (!drag_is_puck && !allow_target) {
+ return TRUE;
+ }
+
+ if (allow_x || !drag_is_puck) {
+ float new_x;
+ x = min (evx, width - 1);
+ x = max (x, 0);
+ new_x = (float) x / (width - 1);
+ if (new_x != drag_target->x) {
+ drag_target->x = new_x;
+ need_move = true;
+ }
+ }
+
+ if (allow_y || drag_is_puck) {
+ float new_y;
+ y = min (evy, height - 1);
+ y = max (y, 0);
+ new_y = (float) y / (height - 1);
+ if (new_y != drag_target->y) {
+ drag_target->y = new_y;
+ need_move = true;
+ }
+ }
+
+ if (need_move) {
+ queue_draw ();
+
+ if (drag_is_puck) {
+
+ panner[drag_index]->set_position (drag_target->x, drag_target->y);
+
+ } else {
+
+ TargetMoved (drag_index);
+ }
+ }
+
+ return TRUE;
+}
+
+gint
+Panner2d::expose_event_impl (GdkEventExpose *event)
+{
+ gint x, y;
+ float fx, fy;
+
+ /* redraw the background */
+
+ get_window().draw_rectangle (get_style()->get_bg_gc(get_state()),
+ true,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+
+ if (!panner.bypassed()) {
+
+ for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
+
+ Target* puck = i->second;
+
+ if (puck->visible) {
+ /* redraw puck */
+
+ fx = min (puck->x, 1.0f);
+ fx = max (fx, -1.0f);
+ x = (gint) floor (width * fx - 4);
+
+ fy = min (puck->y, 1.0f);
+ fy = max (fy, -1.0f);
+ y = (gint) floor (height * fy - 4);
+
+ get_window().draw_arc (get_style()->get_fg_gc(GTK_STATE_NORMAL),
+ true,
+ x, y,
+ 8, 8,
+ 0, 360 * 64);
+ get_window().draw_text (get_style()->get_font(),
+ get_style()->get_fg_gc(GTK_STATE_NORMAL),
+ x + 6, y + 6,
+ puck->text,
+ puck->textlen);
+ }
+ }
+
+ /* redraw any visible targets */
+
+ for (Targets::iterator i = targets.begin(); i != targets.end(); ++i) {
+ Target *target = i->second;
+
+ if (target->visible) {
+
+ /* why -8 ??? why is this necessary ? */
+
+ fx = min (target->x, 1.0f);
+ fx = max (fx, -1.0f);
+ x = (gint) floor ((width - 8) * fx);
+
+ fy = min (target->y, 1.0f);
+ fy = max (fy, -1.0f);
+ y = (gint) floor ((height - 8) * fy);
+
+ get_window().draw_rectangle (get_style()->get_fg_gc(GTK_STATE_ACTIVE),
+ true,
+ x, y,
+ 4, 4);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+gint
+Panner2d::button_press_event_impl (GdkEventButton *ev)
+{
+ switch (ev->button) {
+ case 1:
+ gint x, y;
+ GdkModifierType state;
+
+ drag_target = find_closest_object (ev->x, ev->y, drag_index, drag_is_puck);
+
+ x = (int) floor (ev->x);
+ y = (int) floor (ev->y);
+ state = (GdkModifierType) ev->state;
+
+ return handle_motion (x, y, state);
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+gint
+Panner2d::button_release_event_impl (GdkEventButton *ev)
+{
+ switch (ev->button) {
+ case 1:
+ gint x, y;
+ int ret;
+ GdkModifierType state;
+
+ x = (int) floor (ev->x);
+ y = (int) floor (ev->y);
+ state = (GdkModifierType) ev->state;
+
+ if (drag_is_puck && (Keyboard::modifier_state_contains (state, Keyboard::Shift))) {
+
+ for (Targets::iterator i = pucks.begin(); i != pucks.end(); ++i) {
+ Target* puck = i->second;
+ puck->x = 0.5;
+ puck->y = 0.5;
+ }
+
+ queue_draw ();
+ PuckMoved (-1);
+ ret = TRUE;
+
+ } else {
+ ret = handle_motion (x, y, state);
+ }
+
+ drag_target = 0;
+
+ return ret;
+ break;
+ case 2:
+ toggle_bypass ();
+ return TRUE;
+
+ case 3:
+ show_context_menu ();
+ break;
+
+ }
+
+ return FALSE;
+}
+
+void
+Panner2d::toggle_bypass ()
+{
+ if (bypass_menu_item && (panner.bypassed() != bypass_menu_item->get_active())) {
+ panner.set_bypassed (!panner.bypassed());
+ }
+}
+
+void
+Panner2d::show_context_menu ()
+{
+ using namespace Menu_Helpers;
+
+ if (context_menu == 0) {
+ context_menu = manage (new Menu);
+ context_menu->set_name ("ArdourContextMenu");
+ MenuList& items = context_menu->items();
+
+ items.push_back (CheckMenuElem (_("Bypass")));
+ bypass_menu_item = static_cast<CheckMenuItem*> (items.back());
+ bypass_menu_item->toggled.connect (slot (*this, &Panner2d::toggle_bypass));
+
+ }
+
+ bypass_menu_item->set_active (panner.bypassed());
+ context_menu->popup (1, 0);
+}
+
+void
+Panner2d::allow_x_motion (bool yn)
+{
+ allow_x = yn;
+}
+
+void
+Panner2d::allow_target_motion (bool yn)
+{
+ allow_target = yn;
+}
+
+void
+Panner2d::allow_y_motion (bool yn)
+{
+ allow_y = yn;
+}
+
+int
+Panner2d::puck_position (int which, float& x, float& y)
+{
+ Targets::iterator i;
+
+ if ((i = pucks.find (which)) != pucks.end()) {
+ x = i->second->x;
+ y = i->second->y;
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+Panner2d::target_position (int which, float& x, float& y)
+{
+ Targets::iterator i;
+
+ if ((i = targets.find (which)) != targets.end()) {
+ x = i->second->x;
+ y = i->second->y;
+ return 0;
+ }
+
+ return -1;
+}
+