diff options
author | Taybin Rutkin <taybin@taybin.com> | 2005-09-25 18:42:24 +0000 |
---|---|---|
committer | Taybin Rutkin <taybin@taybin.com> | 2005-09-25 18:42:24 +0000 |
commit | 209d967b1bb80a9735d690d8f4f0455ecb9970ca (patch) | |
tree | 9d76ddcd7c1ac9d91bb2b1a33d31b66ce4ded5de /gtk2_ardour/panner2d.cc | |
parent | e4b9aed743fc765219ac775905a221c017c88fba (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.cc | 636 |
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; +} + |