1662 lines
40 KiB
C
1662 lines
40 KiB
C
|
||
/*******************************************************************************/
|
||
/* Copyright (C) 2007-2008 Jonathan Moore Liles */
|
||
/* */
|
||
/* 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; see the file COPYING. If not,write to the Free Software */
|
||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||
/*******************************************************************************/
|
||
|
||
/* This is a generic double-buffering, optimizing canvas interface to
|
||
grids (patterns and phrases). It draws only what is necessary to keep
|
||
the display up-to-date. Actual drawing functions are in draw.C */
|
||
#include <FL/Fl.H>
|
||
#include <FL/Fl_Cairo.H>
|
||
|
||
#include "canvas.H"
|
||
#include "pattern.H"
|
||
#include "common.h"
|
||
|
||
#include "non.H"
|
||
#include <FL/fl_draw.H>
|
||
#include <FL/Fl.H>
|
||
#include "gui/ui.H"
|
||
#include <FL/Fl_Panzoomer.H>
|
||
#include <FL/Fl_Slider.H>
|
||
|
||
using namespace MIDI;
|
||
extern UI *ui;
|
||
|
||
extern Fl_Color velocity_colors[];
|
||
const int ruler_height = 14;
|
||
|
||
|
||
|
||
|
||
class Canvas::Canvas_Panzoomer : public Fl_Panzoomer
|
||
{
|
||
Fl_Offscreen backbuffer;
|
||
|
||
public:
|
||
|
||
Canvas_Panzoomer( int X, int Y,int W, int H, const char *L = 0 )
|
||
: Fl_Panzoomer(X,Y,W,H,L)
|
||
{
|
||
backbuffer = 0;
|
||
}
|
||
|
||
Canvas *canvas;
|
||
|
||
private:
|
||
|
||
static void draw_dash ( tick_t x, int y, tick_t l, int color, int selected, void *userdata )
|
||
{
|
||
Canvas_Panzoomer *o = (Canvas_Panzoomer*)userdata;
|
||
|
||
o->draw_dash( x,y,l,color,selected );
|
||
}
|
||
|
||
void draw_dash ( tick_t x, int y, tick_t w, int color, int selected ) const
|
||
{
|
||
if ( selected )
|
||
color = FL_MAGENTA;
|
||
else
|
||
color = velocity_colors[ color ];
|
||
|
||
Canvas *c = canvas;
|
||
|
||
double VS = (double)this->h() / c->m.maxh;
|
||
double HS = (double)this->w() / ( _xmax - _xmin );
|
||
|
||
y = c->ntr( y );
|
||
|
||
if ( y < 0 )
|
||
return;
|
||
|
||
y *= VS;
|
||
|
||
fl_color( fl_color_average( color, FL_GRAY, 0.5 ) );
|
||
|
||
fl_rectf(
|
||
x * HS,
|
||
y,
|
||
(w * HS) + 0.5,
|
||
1 * VS + 0.5 );
|
||
}
|
||
|
||
protected:
|
||
|
||
void draw_background ( int X, int Y, int W, int H )
|
||
{
|
||
|
||
/* DMESSAGE( "%s%s%s%s%s%s", */
|
||
/* damage() & FL_DAMAGE_CHILD ? "CHILD " : "", */
|
||
/* damage() & FL_DAMAGE_ALL ? "ALL " : "", */
|
||
/* damage() & FL_DAMAGE_USER1 ? "USER 1 ": "", */
|
||
/* damage() & FL_DAMAGE_EXPOSE ? "EXPOSE " : "", */
|
||
/* damage() & FL_DAMAGE_SCROLL ? "SCROLL " : "", */
|
||
/* damage() & FL_DAMAGE_OVERLAY ? "OVERLAY " : ""); */
|
||
|
||
if ( ! backbuffer ||
|
||
! ( damage() & FL_DAMAGE_USER1 ) )
|
||
{
|
||
if ( !backbuffer )
|
||
backbuffer = fl_create_offscreen( W, H );
|
||
|
||
DMESSAGE( "redrawing preview" );
|
||
|
||
fl_begin_offscreen(backbuffer);
|
||
|
||
fl_rectf( 0, 0, W, H, color() );
|
||
canvas->m.grid->draw_notes( draw_dash, this );
|
||
|
||
fl_end_offscreen();
|
||
}
|
||
|
||
fl_copy_offscreen( X,Y,W,H,backbuffer,0, 0 );
|
||
}
|
||
public:
|
||
|
||
void resize ( int X, int Y, int W, int H )
|
||
{
|
||
Fl_Panzoomer::resize( X,Y,W,H );
|
||
if ( backbuffer )
|
||
fl_delete_offscreen( backbuffer );
|
||
backbuffer = 0;
|
||
redraw();
|
||
}
|
||
|
||
void draw_overlay ( void )
|
||
{
|
||
Canvas *c = canvas;
|
||
|
||
double HS = (double)w() /( _xmax - _xmin );
|
||
|
||
tick_t new_x = c->grid()->index();
|
||
|
||
fl_color( fl_color_add_alpha( FL_GREEN, 100 ) );
|
||
fl_line( x() + new_x * HS, y(), x() + new_x * HS, y() + h() );
|
||
}
|
||
|
||
};
|
||
|
||
static note_properties *ghost_note = 0;
|
||
|
||
Canvas::Canvas ( int X, int Y, int W, int H, const char *L ) : Fl_Group( X,Y,W,H,L )
|
||
{
|
||
|
||
{ Fl_Box *o = new Fl_Box( X, Y, W, H - 75 );
|
||
/* this is a dummy group where the canvas goes */
|
||
Fl_Group::current()->resizable( o );
|
||
}
|
||
{ Fl_Group *o = new Fl_Group( X, Y + H - 75, W, 75 );
|
||
|
||
{
|
||
Canvas_Panzoomer *o = new Canvas_Panzoomer( X, Y + H - 75, W - 14, 75 );
|
||
o->canvas = this;
|
||
o->box( FL_FLAT_BOX );
|
||
// o->color(fl_color_average( FL_BLACK, FL_WHITE, 0.90 ));
|
||
o->color( FL_BLACK );
|
||
// o->color(FL_BACKGROUND_COLOR);
|
||
// o->type( FL_HORIZONTAL );
|
||
o->callback( cb_scroll, this );
|
||
o->when( FL_WHEN_CHANGED );
|
||
panzoomer = o;
|
||
}
|
||
|
||
{
|
||
Fl_Slider *o = new Fl_Slider( X + W - 14, Y + H - panzoomer->h(), 14, panzoomer->h() );
|
||
o->range( 1, 128 );
|
||
o->step( 1 );
|
||
o->type( FL_VERTICAL );
|
||
o->tooltip( "Vertical Zoom" );
|
||
o->callback( cb_scroll, this );
|
||
vzoom = o;
|
||
}
|
||
o->end();
|
||
}
|
||
|
||
m.origin_x = m.origin_y = m.height = m.width = m.div_w = m.div_h = m.margin_top = m.margin_left = m.playhead = m.w = m.h = m.p1 = m.p2 = m.p3 = m.p4 = 0;
|
||
|
||
m.margin_top = ruler_height;
|
||
|
||
m.draw = false;
|
||
m.ruler_drawn = false;
|
||
m.grid_drawn = false;
|
||
|
||
// m.current = m.previous = NULL;
|
||
|
||
m.row_compact = true;
|
||
|
||
m.maxh = 128;
|
||
|
||
m.vp = NULL;
|
||
|
||
end();
|
||
}
|
||
|
||
Canvas::~Canvas ( )
|
||
{
|
||
|
||
}
|
||
|
||
void
|
||
Canvas::handle_event_change ( void )
|
||
{
|
||
/* mark the song as dirty and pass the signal on */
|
||
song.set_dirty();
|
||
|
||
// panzoomer->redraw();
|
||
|
||
redraw();
|
||
}
|
||
|
||
/** change grid to /g/, returns TRUE if new grid size differs from old */
|
||
void
|
||
Canvas::grid ( Grid *g )
|
||
{
|
||
m.grid = g;
|
||
|
||
if ( ! g )
|
||
return;
|
||
|
||
m.vp = &g->viewport;
|
||
|
||
char *s = m.vp->dump();
|
||
DMESSAGE( "viewport: %s", s );
|
||
free( s );
|
||
|
||
m.ruler_drawn = false;
|
||
|
||
resize_grid();
|
||
|
||
vzoom->range( 1, m.maxh );
|
||
vzoom->value( m.vp->h );
|
||
|
||
update_mapping();
|
||
|
||
/* connect signals */
|
||
/* FIXME: what happens when we do this twice? */
|
||
g->signal_events_change.connect( mem_fun( this, &Canvas::handle_event_change ) );
|
||
g->signal_settings_change.connect( signal_settings_change.make_slot() );
|
||
|
||
redraw();
|
||
|
||
// parent()->redraw();
|
||
// signal_settings_change();
|
||
}
|
||
|
||
/** keep row compaction tables up-to-date */
|
||
void
|
||
Canvas::_update_row_mapping ( void )
|
||
{
|
||
/* reset */
|
||
for ( int i = 128; i-- ; )
|
||
m.rtn[i] = m.ntr[i] = -1;
|
||
|
||
DMESSAGE( "updating row mapping" );
|
||
|
||
/* rebuild */
|
||
int r = 0;
|
||
for ( int n = 0; n < 128; ++n )
|
||
{
|
||
if ( m.grid->row_name( n ) )
|
||
{
|
||
m.rtn[r] = n;
|
||
m.ntr[n] = r;
|
||
++r;
|
||
}
|
||
}
|
||
|
||
if ( m.row_compact && r )
|
||
m.maxh = r;
|
||
else
|
||
m.maxh = 128;
|
||
|
||
m.vp->h = min( m.vp->h, m.maxh );
|
||
|
||
resize_grid();
|
||
}
|
||
|
||
/** update everything about mapping, leaving the viewport alone */
|
||
void
|
||
Canvas::update_mapping ( void )
|
||
{
|
||
_update_row_mapping();
|
||
|
||
adj_size();
|
||
|
||
int old_margin = m.margin_left;
|
||
|
||
m.margin_left = 0;
|
||
|
||
m.draw = false;
|
||
|
||
m.grid->draw_row_names( this );
|
||
|
||
m.draw = true;
|
||
|
||
/* if ( m.margin_left != old_margin ) */
|
||
/* { */
|
||
/* // signal_resize(); */
|
||
/* redraw(); */
|
||
/* } */
|
||
/* else */
|
||
|
||
damage(FL_DAMAGE_USER1);
|
||
|
||
}
|
||
|
||
/** change grid mapping */
|
||
void
|
||
Canvas::changed_mapping ( void )
|
||
{
|
||
update_mapping();
|
||
|
||
m.vp->h = min( m.vp->h, m.maxh );
|
||
|
||
if ( m.vp->y + m.vp->h > m.maxh )
|
||
m.vp->y = (m.maxh / 2) - (m.vp->h / 2);
|
||
}
|
||
|
||
Grid *
|
||
Canvas::grid ( void )
|
||
{
|
||
return m.grid;
|
||
}
|
||
|
||
|
||
/** recalculate node sizes based on physical dimensions */
|
||
void
|
||
Canvas::adj_size ( void )
|
||
{
|
||
if ( ! m.vp )
|
||
return;
|
||
|
||
m.div_w = (m.width - m.margin_left) / m.vp->w;
|
||
m.div_h = (m.height - m.margin_top) / m.vp->h;
|
||
}
|
||
|
||
/** reallocate buffers to match grid dimensions */
|
||
void
|
||
Canvas::resize_grid ( void )
|
||
{
|
||
adj_size();
|
||
|
||
DMESSAGE( "resizing grid %dx%d", m.vp->w, m.vp->h );
|
||
|
||
Grid *g = grid();
|
||
panzoomer->x_value( g->x_to_ts( m.vp->x), g->x_to_ts( m.vp->w ), 0, g->length());
|
||
panzoomer->y_value( m.vp->y, m.vp->h, 0, m.maxh );
|
||
|
||
panzoomer->zoom_range( 2, 16 );
|
||
|
||
// m.vp->w = max( 32, min( (int)(m.vp->w * n), 256 ) );
|
||
|
||
}
|
||
|
||
/** inform the canvas with new phsyical dimensions */
|
||
void
|
||
Canvas::resize ( int x, int y, int w, int h )
|
||
{
|
||
m.origin_x = x;
|
||
m.origin_y = y;
|
||
|
||
m.width = w;
|
||
m.height = h - 75;
|
||
|
||
Fl_Group::resize(x,y,w,h);
|
||
|
||
adj_size();
|
||
}
|
||
|
||
|
||
|
||
/***********/
|
||
/* Drawing */
|
||
/***********/
|
||
|
||
/** is /x/ within the viewport? */
|
||
bool
|
||
Canvas::viewable_x ( int x )
|
||
{
|
||
return x >= m.vp->x && x < m.vp->x + m.vp->w;
|
||
}
|
||
|
||
static
|
||
int
|
||
gui_draw_ruler ( int x, int y, int w, int div_w, int div, int ofs, int p1, int p2 )
|
||
{
|
||
/* Across the top */
|
||
|
||
fl_font( FL_TIMES, ruler_height );
|
||
|
||
int h = ruler_height;
|
||
|
||
fl_color( FL_BACKGROUND_COLOR );
|
||
|
||
w += 100; /* FIXME: hack */
|
||
|
||
// fl_rectf( x, y, x + (div_w * w), y + h );
|
||
fl_rectf( x, y, (div_w * w), h );
|
||
|
||
fl_color( FL_FOREGROUND_COLOR );
|
||
|
||
fl_line( x + div_w / 2, y, x + div_w * w, y );
|
||
|
||
char pat[40];
|
||
int z = div;
|
||
int i;
|
||
for ( i = 0; i < w; i++ )
|
||
{
|
||
int k = ofs + i;
|
||
if ( 0 == k % z )
|
||
{
|
||
int nx = x + (i * div_w) + (div_w / 2);
|
||
|
||
fl_color( FL_FOREGROUND_COLOR );
|
||
|
||
fl_line( nx, y, nx, y + h - 1 );
|
||
|
||
sprintf( pat, "%i", 1 + (k / z ));
|
||
|
||
fl_color( FL_FOREGROUND_COLOR );
|
||
fl_draw( pat, nx + div_w / 2, y + h + 1 / 2 );
|
||
}
|
||
}
|
||
|
||
/* if ( p1 != p2 ) */
|
||
/* { */
|
||
/* if ( p1 >= 0 ) */
|
||
/* { */
|
||
/* if ( p1 < p2 ) */
|
||
/* fl_color( FL_GREEN ); */
|
||
/* else */
|
||
/* fl_color( FL_RED ); */
|
||
|
||
/* fl_rectf( x + (div_w * p1), y + h / 2, div_w, h / 2 ); */
|
||
|
||
/* } */
|
||
/* if ( p2 >= 0 ) */
|
||
/* { */
|
||
/* if ( p2 < p1 ) */
|
||
/* fl_color( FL_GREEN ); */
|
||
/* else */
|
||
/* fl_color( FL_RED ); */
|
||
/* fl_rectf( x + (div_w * p2), y + h / 2, div_w, h / 2 ); */
|
||
|
||
/* } */
|
||
/* } */
|
||
|
||
return h;
|
||
}
|
||
|
||
static
|
||
int
|
||
gui_draw_string ( int x, int y, int w, int h, int color, const char *s, bool draw )
|
||
{
|
||
int rw;
|
||
|
||
if ( ! s )
|
||
return 0;
|
||
|
||
fl_font( FL_COURIER, min( h, 18 ) );
|
||
|
||
rw = fl_width( s );
|
||
|
||
if ( fl_not_clipped( x, y, rw, h ) && draw )
|
||
{
|
||
// fl_rectf( x,y,w,h, FL_BACKGROUND_COLOR );
|
||
|
||
if ( color )
|
||
fl_color( velocity_colors[ color ] );
|
||
else
|
||
fl_color( FL_DARK_CYAN );
|
||
|
||
fl_draw( s, x, y + h / 2 + fl_descent() );
|
||
}
|
||
|
||
return rw;
|
||
}
|
||
|
||
/** callback called by Grid::draw_row_names() to draw an individual row name */
|
||
void
|
||
Canvas::draw_row_name ( int y, const char *name, int color )
|
||
{
|
||
bool draw = m.draw;
|
||
bool clear = false;
|
||
|
||
y = ntr( y );
|
||
|
||
if ( ! m.row_compact && ! name )
|
||
clear = true;
|
||
|
||
y -= m.vp->y;
|
||
|
||
int bx = m.origin_x;
|
||
int by = m.origin_y + m.margin_top + y * m.div_h;
|
||
int bw = m.margin_left;
|
||
int bh = m.div_h;
|
||
|
||
if ( y < 0 || y >= m.vp->h )
|
||
draw = false;
|
||
|
||
if ( draw && name )
|
||
{
|
||
fl_rectf( bx, by, bw, bh, index(name, '#') ? FL_GRAY : FL_BLACK );
|
||
fl_rect( bx, by, bw, bh, FL_BLACK );
|
||
}
|
||
|
||
m.margin_left = max( m.margin_left, gui_draw_string( bx + 1, by + 2,
|
||
bw - 1, bh - 4,
|
||
color,
|
||
name,
|
||
draw ) );
|
||
}
|
||
|
||
void
|
||
Canvas::draw_mapping ( void )
|
||
{
|
||
int old_margin = m.margin_left;
|
||
|
||
m.margin_left = 0;
|
||
|
||
m.draw = false;
|
||
|
||
m.grid->draw_row_names( this );
|
||
|
||
adj_size();
|
||
|
||
m.draw = true;
|
||
|
||
m.grid->draw_row_names( this );
|
||
}
|
||
|
||
void
|
||
Canvas::draw_ruler ( void )
|
||
{
|
||
m.margin_top = gui_draw_ruler( m.origin_x + m.margin_left,
|
||
m.origin_y,
|
||
m.vp->w,
|
||
m.div_w,
|
||
m.grid->division(),
|
||
m.vp->x,
|
||
m.p1 - m.vp->x,
|
||
m.p2 - m.vp->x );
|
||
}
|
||
|
||
void
|
||
Canvas::damage_grid ( tick_t x, int y, tick_t w, int h = 1 )
|
||
{
|
||
y = ntr( y );
|
||
|
||
if ( y < 0 )
|
||
return;
|
||
|
||
// adjust for viewport.
|
||
|
||
x = m.grid->ts_to_x(x);
|
||
w = m.grid->ts_to_x(w);
|
||
|
||
x -= m.vp->x;
|
||
y -= m.vp->y;
|
||
|
||
if ( x < 0 || y < 0 || x >= m.vp->w || y >= m.vp->h )
|
||
return;
|
||
|
||
damage(FL_DAMAGE_USER1, m.origin_x + m.margin_left + x * m.div_w,
|
||
m.origin_y + m.margin_top + y * m.div_h,
|
||
m.div_w * w,
|
||
m.div_h * h );
|
||
}
|
||
|
||
void
|
||
Canvas::draw_dash ( tick_t x, int y, tick_t w, int color, int selected ) const
|
||
{
|
||
if ( m.grid->velocity_sensitive() )
|
||
color = velocity_colors[ color ];
|
||
else
|
||
color = velocity_colors[ 127 ];
|
||
|
||
y = ntr( y );
|
||
|
||
if ( y < 0 )
|
||
return;
|
||
|
||
// adjust for viewport.
|
||
|
||
x = m.grid->ts_to_x(x);
|
||
w = m.grid->ts_to_x(w);
|
||
|
||
x -= m.vp->x;
|
||
y -= m.vp->y;
|
||
|
||
x = m.origin_x + m.margin_left + x * m.div_w;
|
||
y = m.origin_y + m.margin_top + y * m.div_h;
|
||
w *= m.div_w;
|
||
|
||
/* fl_rectf( x, y + 1, w, m.div_h - 1, fl_color_add_alpha( color, 170 ) ); */
|
||
|
||
/* fl_rect( x, y + 1, w, m.div_h - 1, selected ? FL_MAGENTA : fl_lighter( FL_BACKGROUND_COLOR )); */
|
||
|
||
fl_draw_box( FL_ROUNDED_BOX, x, y + 1, w, m.div_h - 1, color );
|
||
if ( selected )
|
||
fl_draw_box( FL_ROUNDED_FRAME, x, y + 1, w, m.div_h - 1, FL_MAGENTA );
|
||
|
||
// fl_color_add_alpha( color, 170 ));
|
||
}
|
||
|
||
/** callback used by Grid::draw() */
|
||
void
|
||
Canvas::draw_dash ( tick_t x, int y, tick_t w, int color, int selected, void *userdata )
|
||
{
|
||
Canvas *o = (Canvas*)userdata;
|
||
|
||
o->draw_dash( x,y,w,color,selected );
|
||
}
|
||
|
||
int
|
||
Canvas::playhead_moved ( void )
|
||
{
|
||
int x = m.grid->ts_to_x( m.grid->index() );
|
||
|
||
return m.playhead != x;
|
||
}
|
||
|
||
void
|
||
Canvas::redraw_playhead ( void )
|
||
{
|
||
int old_x = m.playhead;
|
||
|
||
int new_x = m.grid->ts_to_x( m.grid->index() );
|
||
|
||
if ( old_x != new_x )
|
||
{
|
||
window()->damage( FL_DAMAGE_OVERLAY );
|
||
}
|
||
|
||
if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w )
|
||
{
|
||
if ( config.follow_playhead )
|
||
{
|
||
new_x = m.playhead;
|
||
|
||
panzoomer->x_value( m.grid->index() );
|
||
panzoomer->do_callback();
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
Canvas::draw_overlay ( void )
|
||
{
|
||
if ( ! visible_r() )
|
||
return;
|
||
|
||
fl_push_no_clip();
|
||
|
||
fl_push_clip( x() + m.margin_left,
|
||
y() + m.margin_top,
|
||
w() - m.margin_left,
|
||
h() - panzoomer->h() - m.margin_top );
|
||
|
||
draw_playhead();
|
||
|
||
fl_pop_clip();
|
||
|
||
panzoomer->draw_overlay();
|
||
|
||
fl_pop_clip();
|
||
}
|
||
|
||
/** draw only the playhead--without reexamining the grid */
|
||
void
|
||
Canvas::draw_playhead ( void )
|
||
{
|
||
int x = m.grid->ts_to_x( m.grid->index() );
|
||
|
||
if ( m.playhead == x )
|
||
return;
|
||
|
||
m.playhead = x;
|
||
|
||
if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w )
|
||
return;
|
||
|
||
int px = m.origin_x + m.margin_left + ( x - m.vp->x ) * m.div_w;
|
||
|
||
int X,Y,W,H;
|
||
|
||
X = px;
|
||
Y = m.origin_y + m.margin_top;
|
||
W = m.div_w;
|
||
H = m.origin_y + m.margin_top + m.vp->h * m.div_h;
|
||
|
||
fl_rectf( X,Y,W,H, fl_color_add_alpha( FL_RED, 127 ) );
|
||
}
|
||
|
||
void
|
||
Canvas::draw_clip ( void *v, int X, int Y, int W, int H )
|
||
{
|
||
((Canvas*)v)->draw_clip( X,Y,W,H );
|
||
}
|
||
|
||
void
|
||
Canvas::draw_clip ( int X, int Y, int W, int H )
|
||
{
|
||
box( FL_FLAT_BOX );
|
||
labeltype( FL_NO_LABEL );
|
||
|
||
fl_push_clip( X,Y,W,H );
|
||
|
||
|
||
fl_push_clip( m.origin_x + m.margin_left,
|
||
m.origin_y + m.margin_top,
|
||
w() - m.margin_left,
|
||
h() - m.margin_top - panzoomer->h() );
|
||
|
||
fl_rectf( m.origin_x + m.margin_left, m.origin_y + m.margin_top, w(), h(), FL_BLACK );
|
||
|
||
/* draw bar/beat lines */
|
||
|
||
for ( int gx = m.vp->x;
|
||
gx < m.vp->x + m.vp->w + 200; /* hack */
|
||
gx++ )
|
||
{
|
||
if ( gx % m.grid->division() == 0 )
|
||
fl_color( fl_color_add_alpha( FL_GRAY, 50 ));
|
||
else if ( gx % m.grid->subdivision() == 0 )
|
||
fl_color( fl_color_add_alpha( FL_GRAY, 30 ));
|
||
else
|
||
continue;
|
||
|
||
|
||
fl_rectf( m.origin_x + m.margin_left + ( ( gx - m.vp->x ) * m.div_w ),
|
||
m.origin_y + m.margin_top,
|
||
m.div_w,
|
||
y() + h() - m.margin_top );
|
||
}
|
||
|
||
m.grid->draw_notes( draw_dash, this );
|
||
|
||
if ( ghost_note )
|
||
draw_dash( ghost_note->start,
|
||
ghost_note->note,
|
||
ghost_note->duration,
|
||
ghost_note->velocity,
|
||
1 );
|
||
|
||
fl_color( fl_color_add_alpha( fl_rgb_color( 127,127,127 ), 50 ));
|
||
|
||
/* draw grid */
|
||
|
||
if ( m.div_w > 4 )
|
||
{
|
||
for ( int gx = m.origin_x + m.margin_left;
|
||
gx < x() + w();
|
||
gx += m.div_w )
|
||
fl_line( gx, m.origin_y + m.margin_top, gx, y() + h() );
|
||
}
|
||
|
||
if ( m.div_h > 2 )
|
||
{
|
||
for ( int gy = m.origin_y + m.margin_top;
|
||
gy < y() + h();
|
||
gy += m.div_h )
|
||
fl_line( m.origin_x + m.margin_left, gy, x() + w(), gy );
|
||
}
|
||
|
||
|
||
fl_pop_clip();
|
||
|
||
fl_pop_clip();
|
||
}
|
||
|
||
|
||
/** draw ONLY those nodes necessary to bring the canvas up-to-date with the grid */
|
||
void
|
||
Canvas::draw ( void )
|
||
{
|
||
box( FL_NO_BOX );
|
||
labeltype( FL_NO_LABEL );
|
||
|
||
/* DMESSAGE( "%s%s%s%s%s%s", */
|
||
/* damage() & FL_DAMAGE_CHILD ? "CHILD " : "", */
|
||
/* damage() & FL_DAMAGE_ALL ? "ALL " : "", */
|
||
/* damage() & FL_DAMAGE_USER1 ? "USER 1 ": "", */
|
||
/* damage() & FL_DAMAGE_EXPOSE ? "EXPOSE " : "", */
|
||
/* damage() & FL_DAMAGE_SCROLL ? "SCROLL " : "", */
|
||
/* damage() & FL_DAMAGE_OVERLAY ? "OVERLAY " : ""); */
|
||
|
||
|
||
if ( damage() & FL_DAMAGE_SCROLL )
|
||
{
|
||
draw_ruler();
|
||
|
||
int dx = ( _old_scroll_x - m.vp->x ) * m.div_w;
|
||
int dy = ( _old_scroll_y - m.vp->y ) * m.div_h;
|
||
|
||
fl_scroll( m.origin_x + m.margin_left,
|
||
m.origin_y + m.margin_top,
|
||
w() - m.margin_left,
|
||
h() - m.margin_top - panzoomer->h(),
|
||
dx, dy,
|
||
draw_clip,
|
||
this );
|
||
|
||
if ( dy )
|
||
draw_mapping();
|
||
|
||
_old_scroll_x = m.vp->x;
|
||
_old_scroll_y = m.vp->y;
|
||
|
||
if ( damage() & FL_DAMAGE_CHILD )
|
||
clear_damage( FL_DAMAGE_CHILD );
|
||
}
|
||
else if ( damage() & ~FL_DAMAGE_CHILD )
|
||
{
|
||
draw_mapping();
|
||
draw_ruler();
|
||
|
||
draw_clip( x(), y(), w(), h() );
|
||
}
|
||
|
||
draw_children();
|
||
}
|
||
|
||
void
|
||
Canvas::cb_scroll ( Fl_Widget *w, void *v )
|
||
{
|
||
((Canvas*)v)->cb_scroll( w );
|
||
}
|
||
|
||
void
|
||
Canvas::cb_scroll ( Fl_Widget *w )
|
||
{
|
||
if ( w == panzoomer )
|
||
{
|
||
Fl_Panzoomer *o = (Fl_Panzoomer*)w;
|
||
|
||
_old_scroll_x = m.vp->x;
|
||
|
||
m.vp->x = grid()->ts_to_x( o->x_value() );
|
||
m.vp->y = o->y_value();
|
||
|
||
damage( FL_DAMAGE_SCROLL );
|
||
|
||
if ( o->zoom_changed() )
|
||
{
|
||
m.vp->w = m.grid->division() * o->zoom();
|
||
resize_grid();
|
||
redraw();
|
||
}
|
||
}
|
||
else if ( w == vzoom )
|
||
{
|
||
Fl_Slider *o = (Fl_Slider*)w;
|
||
|
||
float n = o->value();
|
||
|
||
m.vp->h = min( (int)n, m.maxh );
|
||
|
||
resize_grid();
|
||
|
||
song.set_dirty();
|
||
|
||
redraw();
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/** convert pixel coords into grid coords. returns true if valid */
|
||
bool
|
||
Canvas::grid_pos ( int *x, int *y ) const
|
||
{
|
||
/* if ( ( *x < m.origin_x + m.margin_left ) || */
|
||
/* ( *y < m.origin_y + m.margin_top ) || */
|
||
/* ( *x > m.origin_x + w() ) || */
|
||
/* (*y > m.origin_y + h() - panzoomer->h() ) ) */
|
||
/* return false; */
|
||
|
||
*y = (*y - m.margin_top - m.origin_y) / m.div_h;
|
||
*x = (*x - m.margin_left - m.origin_x) / m.div_w;
|
||
|
||
/* if ( *x < 0 || *y < 0 || *x >= m.vp->w || *y >= m.vp->h ) */
|
||
/* return false; */
|
||
|
||
/* adjust for viewport */
|
||
*x += m.vp->x;
|
||
*y += m.vp->y;
|
||
|
||
/* adjust for row-compaction */
|
||
*y = rtn( *y );
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
|
||
/******************/
|
||
/* Input handlers */
|
||
/******************/
|
||
|
||
/* These methods translate viewport pixel coords to absolute grid
|
||
coords and pass on to the grid. */
|
||
|
||
/** if coords correspond to a row name entry, return the (absolute) note number, otherwise return -1 */
|
||
int
|
||
Canvas::is_row_press ( void ) const
|
||
{
|
||
if ( Fl::event_inside( this->x(),
|
||
this->y() + this->m.margin_top,
|
||
this->m.margin_left,
|
||
( this->h() - this->m.margin_top ) - this->panzoomer->h() ) )
|
||
{
|
||
int dx,dy;
|
||
dx = Fl::event_x();
|
||
dy = Fl::event_y();
|
||
|
||
grid_pos( &dx, &dy );
|
||
|
||
return m.grid->y_to_note(dy );
|
||
}
|
||
else
|
||
return -1;
|
||
}
|
||
|
||
void
|
||
Canvas::start_cursor ( int x, int y )
|
||
{
|
||
if ( ! grid_pos( &x, &y ) )
|
||
return;
|
||
|
||
m.ruler_drawn = false;
|
||
|
||
m.p1 = x;
|
||
m.p3 = ntr( y );
|
||
|
||
_lr();
|
||
|
||
redraw();
|
||
}
|
||
|
||
void
|
||
Canvas::end_cursor ( int x, int y )
|
||
{
|
||
if ( ! grid_pos( &x, &y ) )
|
||
return;
|
||
|
||
m.ruler_drawn = false;
|
||
|
||
m.p2 = x;
|
||
m.p4 = ntr( y );
|
||
|
||
_lr();
|
||
|
||
redraw();
|
||
}
|
||
|
||
void
|
||
Canvas::adj_color ( int x, int y, int n )
|
||
{
|
||
if ( ! grid_pos( &x, &y ) )
|
||
return;
|
||
|
||
m.grid->adj_velocity( x, y, n );
|
||
}
|
||
|
||
void
|
||
Canvas::adj_length ( int x, int y, int n )
|
||
{
|
||
if ( ! grid_pos( &x, &y ) )
|
||
return;
|
||
|
||
m.grid->adj_duration( x, y, n );
|
||
}
|
||
|
||
void
|
||
Canvas::set_end ( int x, int y, int n )
|
||
{
|
||
if ( ! grid_pos( &x, &y ) )
|
||
return;
|
||
|
||
m.grid->set_end( x, y, n );
|
||
}
|
||
|
||
void
|
||
Canvas::select ( int x, int y )
|
||
{
|
||
if ( ! grid_pos( &x, &y ) )
|
||
return;
|
||
|
||
m.grid->toggle_select( x, y );
|
||
}
|
||
|
||
void
|
||
Canvas::move_selected ( int dir, int n )
|
||
{
|
||
switch ( dir )
|
||
{
|
||
case RIGHT:
|
||
m.grid->move_selected( n );
|
||
break;
|
||
case LEFT:
|
||
m.grid->move_selected( 0 - n );
|
||
break;
|
||
case UP:
|
||
case DOWN:
|
||
{
|
||
/* row-compaction makes this a little complicated */
|
||
event_list *el = m.grid->events();
|
||
|
||
/* FIXME: don't allow movement beyond the edges! */
|
||
|
||
/* int hi, lo; */
|
||
|
||
/* m.grid->selected_hi_lo_note( &hi, &lo ); */
|
||
|
||
/* hi = ntr( hi ) > 0 ? ntr( hi ) : */
|
||
|
||
/* if ( m.grid->y_to_note( ntr( hi ) ) ) */
|
||
|
||
|
||
if ( dir == UP )
|
||
for ( int y = 0; y <= m.maxh; ++y )
|
||
el->rewrite_selected( m.grid->y_to_note( rtn( y ) ), m.grid->y_to_note( rtn( y - n ) ) );
|
||
else
|
||
for ( int y = m.maxh; y >= 0; --y )
|
||
el->rewrite_selected( m.grid->y_to_note( rtn( y ) ), m.grid->y_to_note( rtn( y + n ) ) );
|
||
|
||
m.grid->events( el );
|
||
|
||
delete el;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
Canvas::randomize_row ( int y )
|
||
{
|
||
int x = m.margin_left;
|
||
|
||
if ( ! grid_pos( &x, &y ) )
|
||
return;
|
||
|
||
((pattern*)m.grid)->randomize_row( y, song.random.feel, song.random.probability );
|
||
}
|
||
|
||
void
|
||
Canvas::_lr ( void )
|
||
{
|
||
int l, r;
|
||
|
||
if ( m.p2 > m.p1 )
|
||
{
|
||
l = m.p1;
|
||
r = m.p2;
|
||
}
|
||
else
|
||
{
|
||
l = m.p2;
|
||
r = m.p1;
|
||
}
|
||
|
||
m.p1 = l;
|
||
m.p2 = r;
|
||
}
|
||
|
||
void
|
||
Canvas::select_range ( void )
|
||
{
|
||
if ( m.p3 == m.p4 )
|
||
m.grid->select( m.p1, m.p2 );
|
||
else
|
||
m.grid->select( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
|
||
}
|
||
|
||
void
|
||
Canvas::invert_selection ( void )
|
||
{
|
||
m.grid->invert_selection();
|
||
}
|
||
|
||
void
|
||
Canvas::crop ( void )
|
||
{
|
||
if ( m.p3 == m.p4 )
|
||
m.grid->crop( m.p1, m.p2 );
|
||
else
|
||
m.grid->crop( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
|
||
|
||
m.vp->x = 0;
|
||
|
||
m.p2 = m.p2 - m.p1;
|
||
m.p1 = 0;
|
||
|
||
m.ruler_drawn = false;
|
||
}
|
||
|
||
void
|
||
Canvas::delete_time ( void )
|
||
{
|
||
m.grid->delete_time( m.p1, m.p2 );
|
||
}
|
||
|
||
|
||
void
|
||
Canvas::insert_time ( void )
|
||
{
|
||
m.grid->insert_time( m.p1, m.p2 );
|
||
}
|
||
|
||
/** paste range as new grid */
|
||
void
|
||
Canvas::duplicate_range ( void )
|
||
{
|
||
Grid *g = m.grid->clone();
|
||
|
||
g->crop( m.p1, m.p2 );
|
||
g->viewport.x = 0;
|
||
}
|
||
|
||
void
|
||
Canvas::row_compact ( int n )
|
||
{
|
||
switch ( n )
|
||
{
|
||
case OFF:
|
||
m.row_compact = false;
|
||
m.maxh = 128;
|
||
break;
|
||
case ON:
|
||
m.row_compact = true;
|
||
m.vp->y = 0;
|
||
_update_row_mapping();
|
||
break;
|
||
case TOGGLE:
|
||
row_compact( m.row_compact ? OFF : ON );
|
||
break;
|
||
}
|
||
// _reset();
|
||
}
|
||
|
||
void
|
||
Canvas::pan ( int dir, int n )
|
||
{
|
||
|
||
switch ( dir )
|
||
{
|
||
case LEFT: case RIGHT: case TO_PLAYHEAD: case TO_NEXT_NOTE: case TO_PREV_NOTE:
|
||
/* handle horizontal movement specially */
|
||
n *= m.grid->division();
|
||
m.ruler_drawn = false;
|
||
break;
|
||
default:
|
||
n *= 5;
|
||
break;
|
||
}
|
||
|
||
switch ( dir )
|
||
{
|
||
case LEFT:
|
||
m.vp->x = max( m.vp->x - n, 0 );
|
||
break;
|
||
case RIGHT:
|
||
m.vp->x += n;
|
||
break;
|
||
case TO_PLAYHEAD:
|
||
m.vp->x = m.playhead - (m.playhead % m.grid->division());
|
||
break;
|
||
case UP:
|
||
m.vp->y = max( m.vp->y - n, 0 );
|
||
break;
|
||
case DOWN:
|
||
m.vp->y = min( m.vp->y + n, m.maxh - m.vp->h );
|
||
break;
|
||
case TO_NEXT_NOTE:
|
||
{
|
||
int x = m.grid->next_note_x( m.vp->x );
|
||
m.vp->x = x - (x % m.grid->division() );
|
||
break;
|
||
}
|
||
case TO_PREV_NOTE:
|
||
{
|
||
int x = m.grid->prev_note_x( m.vp->x );
|
||
m.vp->x = x - (x % m.grid->division() );
|
||
break;
|
||
}
|
||
}
|
||
|
||
damage(FL_DAMAGE_USER1);
|
||
}
|
||
|
||
void
|
||
Canvas::can_scroll ( int *left, int *right, int *up, int *down )
|
||
{
|
||
*left = m.vp->x;
|
||
*right = -1;
|
||
*up = m.vp->y;
|
||
*down = m.maxh - ( m.vp->y + m.vp->h );
|
||
}
|
||
|
||
|
||
/** adjust horizontal zoom (* n) */
|
||
void
|
||
Canvas::h_zoom ( float n )
|
||
{
|
||
m.vp->w = max( 32, min( (int)(m.vp->w * n), 256 ) );
|
||
|
||
resize_grid();
|
||
|
||
song.set_dirty();
|
||
}
|
||
|
||
void
|
||
Canvas::v_zoom_fit ( void )
|
||
{
|
||
if ( ! m.grid )
|
||
return;
|
||
|
||
changed_mapping();
|
||
|
||
m.vp->h = m.maxh;
|
||
m.vp->y = 0;
|
||
|
||
resize_grid();
|
||
|
||
song.set_dirty();
|
||
|
||
}
|
||
|
||
/** adjust vertical zoom (* n) */
|
||
void
|
||
Canvas::v_zoom ( float n )
|
||
{
|
||
m.vp->h = max( 1, min( (int)(m.vp->h * n), m.maxh ) );
|
||
|
||
resize_grid();
|
||
|
||
song.set_dirty();
|
||
}
|
||
|
||
void
|
||
Canvas::notes ( char *s )
|
||
{
|
||
m.grid->notes( s );
|
||
}
|
||
|
||
char *
|
||
Canvas::notes ( void )
|
||
{
|
||
return m.grid->notes();
|
||
}
|
||
|
||
int
|
||
Canvas::handle ( int m )
|
||
{
|
||
Canvas *c = this;
|
||
|
||
int ow, oh;
|
||
|
||
int x, y;
|
||
int processed = 1;
|
||
|
||
x = Fl::event_x();
|
||
y = Fl::event_y();
|
||
|
||
static int drag_x;
|
||
static int drag_y;
|
||
static bool delete_note;
|
||
static note_properties *drag_note;
|
||
static int adjusting_velocity;
|
||
|
||
// static note_properties;
|
||
|
||
ow = c->grid()->viewport.w;
|
||
oh = c->grid()->viewport.h;
|
||
|
||
switch ( m )
|
||
{
|
||
case FL_FOCUS:
|
||
case FL_UNFOCUS:
|
||
return 1;
|
||
case FL_ENTER:
|
||
case FL_LEAVE:
|
||
fl_cursor( FL_CURSOR_DEFAULT );
|
||
return 1;
|
||
case FL_MOVE:
|
||
{
|
||
if ( Fl::event_inside( this->x() + this->m.margin_left,
|
||
this->y() + this->m.margin_top,
|
||
this->w() - this->m.margin_left,
|
||
( this->h() - this->m.margin_top ) - this->panzoomer->h() ) )
|
||
fl_cursor( FL_CURSOR_HAND );
|
||
else
|
||
fl_cursor( FL_CURSOR_DEFAULT );
|
||
|
||
return 1;
|
||
break;
|
||
}
|
||
case FL_KEYBOARD:
|
||
{
|
||
|
||
/* if ( Fl::event_state() & FL_ALT || Fl::event_state() & FL_CTRL ) */
|
||
/* // this is more than a simple keypress. */
|
||
/* return 0; */
|
||
|
||
|
||
if ( Fl::event_state() & FL_CTRL )
|
||
{
|
||
switch ( Fl::event_key() )
|
||
{
|
||
case FL_Delete:
|
||
c->delete_time();
|
||
break;
|
||
case FL_Insert:
|
||
c->insert_time();
|
||
break;
|
||
case FL_Right:
|
||
c->pan( TO_NEXT_NOTE, 0 );
|
||
break;
|
||
case FL_Left:
|
||
c->pan( TO_PREV_NOTE, 0 );
|
||
break;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
else
|
||
if ( Fl::event_state() & FL_ALT )
|
||
return 0;
|
||
|
||
switch ( Fl::event_key() )
|
||
{
|
||
case FL_Left:
|
||
c->pan( LEFT, 1 );
|
||
break;
|
||
case FL_Right:
|
||
c->pan( RIGHT, 1 );
|
||
break;
|
||
case FL_Up:
|
||
c->pan( UP, 1 );
|
||
break;
|
||
case FL_Down:
|
||
c->pan( DOWN, 1 );
|
||
break;
|
||
default:
|
||
/* have to do this to get shifted keys */
|
||
switch ( *Fl::event_text() )
|
||
{
|
||
case 'f':
|
||
c->pan( TO_PLAYHEAD, 0 );
|
||
break;
|
||
case 'r':
|
||
c->select_range();
|
||
break;
|
||
case 'q':
|
||
c->grid()->select_none();
|
||
break;
|
||
case 'i':
|
||
c->invert_selection();
|
||
break;
|
||
/* case '1': */
|
||
/* c->h_zoom( 2.0f ); */
|
||
/* break; */
|
||
/* case '2': */
|
||
/* c->h_zoom( 0.5f ); */
|
||
/* break; */
|
||
/* case '3': */
|
||
/* c->v_zoom( 2.0f ); */
|
||
/* break; */
|
||
/* case '4': */
|
||
/* c->v_zoom( 0.5f ); */
|
||
/* break; */
|
||
/* case ' ': */
|
||
/* transport.toggle(); */
|
||
/* break; */
|
||
|
||
#define IS_PATTERN (parent() == ui->pattern_tab)
|
||
#define IS_PHRASE (parent() == ui->phrase_tab)
|
||
#define IS_SEQUENCE (parent() == ui->sequence_tab)
|
||
case '<':
|
||
c->move_selected( LEFT, 1 );
|
||
break;
|
||
case '>':
|
||
c->move_selected( RIGHT, 1 );
|
||
break;
|
||
case ',':
|
||
c->move_selected( UP, 1 );
|
||
break;
|
||
case '.':
|
||
c->move_selected( DOWN, 1 );
|
||
break;
|
||
case 'C':
|
||
c->crop();
|
||
break;
|
||
case 'd':
|
||
{
|
||
MESSAGE( "duplicating thing" );
|
||
c->grid( c->grid()->clone() );
|
||
|
||
// number of phrases may have changed.
|
||
ui->update_sequence_widgets();
|
||
|
||
break;
|
||
|
||
}
|
||
case 'D':
|
||
c->duplicate_range();
|
||
break;
|
||
case 't':
|
||
c->grid()->trim();
|
||
break;
|
||
default:
|
||
processed = 0;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
case FL_PUSH:
|
||
{
|
||
switch ( Fl::event_button() )
|
||
{
|
||
case 1:
|
||
{
|
||
delete_note = true;
|
||
adjusting_velocity = 0;
|
||
|
||
if ( Fl::event_ctrl() )
|
||
{
|
||
c->select( x, y );
|
||
processed = 2;
|
||
break;
|
||
}
|
||
|
||
int dx = x;
|
||
int dy = y;
|
||
|
||
grid_pos( &dx, &dy );
|
||
|
||
int note;
|
||
if ( ( note = c->is_row_press() ) >= 0 )
|
||
{
|
||
if ( IS_PATTERN )
|
||
((pattern *)c->grid())->row_name_press( note );
|
||
|
||
processed = 2;
|
||
break;
|
||
}
|
||
|
||
if ( Fl::event_inside( this->x() + this->m.margin_left,
|
||
this->y() + this->m.margin_top,
|
||
this->w() - this->m.margin_left,
|
||
( this->h() - this->m.margin_top ) - this->panzoomer->h() ) )
|
||
{
|
||
|
||
if ( ! this->m.grid->is_set( dx,dy ))
|
||
{
|
||
ghost_note = new note_properties;
|
||
|
||
ghost_note->start = this->m.grid->x_to_ts( dx );
|
||
ghost_note->note = dy;
|
||
ghost_note->duration = this->m.grid->default_length();
|
||
ghost_note->velocity = 64;
|
||
|
||
delete_note = false;
|
||
|
||
processed = 1;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
ghost_note = new note_properties;
|
||
drag_note = new note_properties;
|
||
this->m.grid->get_note_properties( dx, dy, ghost_note );
|
||
this->m.grid->get_note_properties( dx, dy, drag_note );
|
||
this->m.grid->del( dx, dy );
|
||
|
||
delete_note = true;
|
||
}
|
||
|
||
this->m.grid->get_start( &dx, &dy );
|
||
|
||
drag_x = x;
|
||
drag_y = y;
|
||
|
||
take_focus();
|
||
}
|
||
else
|
||
processed = 0;
|
||
|
||
break;
|
||
}
|
||
case 3:
|
||
{
|
||
int note;
|
||
if ( ( note = is_row_press() ) >= 0 )
|
||
{
|
||
/* inside the note headings */
|
||
|
||
DMESSAGE( "click on row %d", note );
|
||
if ( IS_PATTERN )
|
||
{
|
||
Instrument *i = ((pattern *)c->grid())->mapping.instrument();
|
||
|
||
if ( i )
|
||
{
|
||
ui->edit_instrument_row( i, note );
|
||
|
||
c->changed_mapping();
|
||
}
|
||
}
|
||
}
|
||
else
|
||
if ( Fl::event_state() & FL_SHIFT )
|
||
{
|
||
c->end_cursor( x, y );
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
processed = 0;
|
||
}
|
||
break;
|
||
}
|
||
case FL_RELEASE:
|
||
switch ( Fl::event_button() )
|
||
{
|
||
case 1:
|
||
{
|
||
int dx = x;
|
||
int dy = y;
|
||
grid_pos( &dx, &dy );
|
||
|
||
if ( IS_PATTERN && Fl::event_state() & ( FL_ALT | FL_CTRL ) )
|
||
c->randomize_row( y );
|
||
else
|
||
{
|
||
if ( delete_note )
|
||
{
|
||
// this->m.grid->del( dx, dy );
|
||
if ( ghost_note )
|
||
{
|
||
damage_grid( ghost_note->start, ghost_note->note, ghost_note->duration, 1 );
|
||
|
||
delete ghost_note;
|
||
}
|
||
ghost_note = 0;
|
||
}
|
||
else
|
||
if ( ghost_note )
|
||
{
|
||
this->m.grid->put( this->m.grid->ts_to_x( ghost_note->start ),
|
||
ghost_note->note,
|
||
ghost_note->duration,
|
||
ghost_note->velocity);
|
||
|
||
delete_note = false;
|
||
|
||
delete ghost_note;
|
||
ghost_note = 0;
|
||
}
|
||
}
|
||
|
||
if ( drag_note )
|
||
delete drag_note;
|
||
drag_note = 0;
|
||
|
||
break;
|
||
}
|
||
default:
|
||
processed = 0;
|
||
break;
|
||
}
|
||
break;
|
||
case FL_DRAG:
|
||
if ( Fl::event_button1() )
|
||
{
|
||
int dx = x;
|
||
int dy = y;
|
||
|
||
grid_pos( &dx, &dy );
|
||
|
||
if ( ghost_note )
|
||
{
|
||
damage_grid( ghost_note->start, ghost_note->note, ghost_note->duration, 1 );
|
||
|
||
if ( drag_note )
|
||
{
|
||
int ody = drag_y;
|
||
int odx = drag_x;
|
||
|
||
grid_pos( &odx, &ody );
|
||
|
||
if ( ody != dy )
|
||
{
|
||
adjusting_velocity = 1;
|
||
|
||
ghost_note->velocity =
|
||
drag_note->velocity +
|
||
( (drag_y - y) / 3.0f );
|
||
|
||
if ( ghost_note->velocity < 0 )
|
||
ghost_note->velocity = 0;
|
||
else if ( ghost_note->velocity > 127 )
|
||
ghost_note->velocity = 127;
|
||
}
|
||
}
|
||
|
||
if ( ! adjusting_velocity )
|
||
{
|
||
if ( dx > this->m.grid->ts_to_x( ghost_note->start ) )
|
||
{
|
||
ghost_note->duration = this->m.grid->x_to_ts( dx ) - ghost_note->start;
|
||
}
|
||
|
||
damage_grid( ghost_note->start, ghost_note->note, ghost_note->duration, 1 );
|
||
}
|
||
else
|
||
ghost_note->duration = drag_note->duration;
|
||
|
||
delete_note = false;
|
||
|
||
processed = 2;
|
||
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
processed = 0;
|
||
}
|
||
|
||
int nw, nh;
|
||
nw = c->grid()->viewport.w;
|
||
nh = c->grid()->viewport.h;
|
||
|
||
if ( processed )
|
||
window()->damage(FL_DAMAGE_OVERLAY);
|
||
|
||
if ( processed == 1 )
|
||
damage(FL_DAMAGE_USER1);
|
||
|
||
if ( ! processed )
|
||
return Fl_Group::handle( m );
|
||
|
||
return processed;
|
||
}
|