2 /*******************************************************************************/
3 /* Copyright (C) 2007-2008 Jonathan Moore Liles */
5 /* This program is free software; you can redistribute it and/or modify it */
6 /* under the terms of the GNU General Public License as published by the */
7 /* Free Software Foundation; either version 2 of the License, or (at your */
8 /* option) any later version. */
10 /* This program is distributed in the hope that it will be useful, but WITHOUT */
11 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
12 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
15 /* You should have received a copy of the GNU General Public License along */
16 /* with This program; see the file COPYING. If not,write to the Free Software */
17 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /*******************************************************************************/
20 /* This is a generic double-buffering, optimizing canvas interface to
21 grids (patterns and phrases). It draws only what is necessary to keep
22 the display up-to-date. Actual drawing functions are in draw.C */
32 Canvas::_alloc_array ( void )
36 int one = sizeof( typeof( a ) ) * m.vp->w;
37 int two = sizeof( typeof( a[0] ) ) * (m.vp->h * m.vp->w);
39 a = (cell_t **) malloc( one + two );
43 cell_t *c = (cell_t *) (((unsigned char *)a) + one);
45 for ( uint x = m.vp->w; x-- ; )
49 for ( uint y = m.vp->h; y-- ; )
51 a[ x ][ y ].flags = 0;
52 a[ x ][ y ].state = -1;
53 a[ x ][ y ].color = 0;
65 m.origin_x = m.origin_y = m.height = m.width = m.div_w = m.div_h = m.playhead = m.margin_top = m.margin_left = m.playhead = m.w = m.h = m.p1 = m.p2 = m.p3 = m.p4 = 0;
67 m.margin_top = ruler_height;
70 m.ruler_drawn = false;
71 m.mapping_drawn = false;
74 m.current = m.previous = NULL;
84 Canvas::handle_event_change ( void )
86 /* mark the song as dirty and pass the signal on */
92 /** change grid to /g/, returns TRUE if new grid size differs from old */
94 Canvas::grid ( Grid *g )
103 char *s = m.vp->dump();
104 DMESSAGE( "viewport: %s", s );
107 m.ruler_drawn = false;
113 // m.shape = m.grid->draw_shape();
115 /* connect signals */
116 /* FIXME: what happens when we do this twice? */
117 g->signal_events_change.connect( mem_fun( this, &Canvas::handle_event_change ) );
118 g->signal_settings_change.connect( signal_settings_change.make_slot() );
121 signal_settings_change();
125 /** keep row compaction tables up-to-date */
127 Canvas::_update_row_mapping ( void )
130 for ( int i = 128; i-- ; )
131 m.rtn[i] = m.ntr[i] = -1;
133 DMESSAGE( "updating row mapping" );
137 for ( int n = 0; n < 128; ++n )
139 if ( m.grid->row_name( n ) )
147 if ( m.row_compact && r )
152 m.vp->h = min( m.vp->h, m.maxh );
155 /** update everything about mapping, leaving the viewport alone */
157 Canvas::update_mapping ( void )
159 _update_row_mapping();
161 m.mapping_drawn = false;
165 int old_margin = m.margin_left;
171 m.grid->draw_row_names( this );
175 if ( m.margin_left != old_margin )
185 /** change grid mapping */
187 Canvas::changed_mapping ( void )
191 m.vp->h = min( m.vp->h, m.maxh );
193 if ( m.vp->y + m.vp->h > m.maxh )
194 m.vp->y = (m.maxh / 2) - (m.vp->h / 2);
200 Canvas::grid ( void )
206 /** recalculate node sizes based on physical dimensions */
208 Canvas::resize ( void )
213 m.div_w = (m.width - m.margin_left) / m.vp->w;
214 m.div_h = (m.height - m.margin_top) / m.vp->h;
216 m.mapping_drawn = m.ruler_drawn = false;
219 /** reallocate buffers to match grid dimensions */
221 Canvas::resize_grid ( void )
223 // _update_row_mapping();
229 if ( m.vp->w != m.w || m.vp->h != m.h ||
230 m.div_w != m.old_div_w || m.div_h != m.old_div_h )
235 m.old_div_w = m.div_w;
236 m.old_div_h = m.div_h;
242 DMESSAGE( "resizing grid %dx%d", m.vp->w, m.vp->h );
250 m.current = _alloc_array();
251 m.previous = _alloc_array();
253 m.grid_drawn = false;
256 /** inform the canvas with new phsyical dimensions */
258 Canvas::resize ( int x, int y, int w, int h )
275 /** copy last buffer into current */
277 Canvas::copy ( void )
279 for ( uint y = m.vp->h; y-- ; )
280 for ( uint x = m.vp->w; x-- ; )
281 m.current[ x ][ y ] = m.previous[ x ][ y ];
285 /** reset last buffer */
287 Canvas::_reset ( void )
289 cell_t empty = {0,0,0};
291 for ( uint y = m.vp->h; y-- ; )
292 for ( uint x = m.vp->w; x-- ; )
293 m.current[ x ][ y ] = empty;
296 /** prepare current buffer for drawing (draw "background") */
298 Canvas::clear ( void )
300 uint rule = m.grid->ppqn();
302 uint lx = m.grid->ts_to_x( m.grid->length() );
304 for ( uint y = m.vp->h; y--; )
305 for ( uint x = m.vp->w; x--; )
307 m.current[ x ][ y ].color = 0;
308 m.current[ x ][ y ].state = EMPTY;
309 m.current[ x ][ y ].flags = 0;
312 for ( int x = m.vp->w - rule; x >= 0; x -= rule )
313 for ( uint y = m.vp->h; y-- ; )
314 m.current[ x ][ y ].state = LINE;
316 int sx = (int)(lx - m.vp->x) >= 0 ? lx - m.vp->x : 0;
318 for ( int x = sx; x < m.vp->w; ++x )
319 for ( int y = m.vp->h; y-- ; )
320 m.current[ x ][ y ].state = PARTIAL;
324 /** is /x/ within the viewport? */
326 Canvas::viewable_x ( int x )
328 return x >= m.vp->x && x < m.vp->x + m.vp->w;
331 /** flush delta of last and current buffers to screen, then flip them */
333 Canvas::flip ( void )
335 /* FIXME: should this not go in clear()? */
338 if ( viewable_x( m.p1 ) ) draw_line( m.p1 - m.vp->x, F_P1 );
339 if ( viewable_x( m.p2 ) ) draw_line( m.p2 - m.vp->x, F_P2 );
342 if ( viewable_x( m.playhead ) ) draw_line( m.playhead - m.vp->x, F_PLAYHEAD );
344 const int shape = m.grid->draw_shape();
346 for ( uint y = m.vp->h; y--; )
347 for ( uint x = m.vp->w; x--; )
349 cell_t *c = &m.current[ x ][ y ];
350 cell_t *p = &m.previous[ x ][ y ];
352 /* draw selection rect */
354 if ( y + m.vp->y >= m.p3 && x + m.vp->x >= m.p1 &&
355 y + m.vp->y <= m.p4 && x + m.vp->x < m.p2 )
356 c->flags |= F_SELECTION;
359 gui_draw_shape( m.origin_x + m.margin_left + x * m.div_w, m.origin_y + m.margin_top + y * m.div_h, m.div_w, m.div_h,
360 shape, c->state, c->flags, c->color );
363 cell_t **tmp = m.previous;
365 m.previous = m.current;
369 /** redraw the ruler at the top of the canvas */
371 Canvas::redraw_ruler ( void )
373 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,
374 m.p1 - m.vp->x, m.p2 - m.vp->x );
375 m.ruler_drawn = true;
378 /** callback called by Grid::draw_row_names() to draw an individual row name */
380 Canvas::draw_row_name ( int y, const char *name, int color )
387 if ( ! m.row_compact && ! name )
393 int by = m.origin_y + m.margin_top + y * m.div_h;
394 int bw = m.margin_left;
397 if ( y < 0 || y >= m.vp->h )
401 gui_clear_area( bx, by, bw, bh );
403 m.margin_left = max( m.margin_left, gui_draw_string( bx, by,
410 /** redraw row names */
412 Canvas::redraw_mapping ( void )
418 m.grid->draw_row_names( this );
424 m.grid->draw_row_names( this );
426 m.mapping_drawn = true;
430 Canvas::draw_mapping ( void )
432 if ( ! m.mapping_drawn ) redraw_mapping();
436 Canvas::draw_ruler ( void )
438 if ( ! m.ruler_drawn ) redraw_ruler();
441 /** "draw" a shape in the backbuffer */
443 Canvas::draw_shape ( int x, int y, int shape, int state, int color, bool selected )
450 // adjust for viewport.
455 if ( x < 0 || y < 0 || x >= m.vp->w || y >= m.vp->h )
458 m.current[ x ][ y ].color = color;
459 m.current[ x ][ y ].state = (uint)m.vp->x + x > m.grid->ts_to_x( m.grid->length() ) ? PARTIAL : state;
461 m.current[ x ][ y ].state = SELECTED;
462 m.current[ x ][ y ].flags = 0;
465 /** callback used by Grid::draw() */
467 Canvas::draw_dash ( int x, int y, int l, int shape, int color, bool selected )
469 draw_shape( x, y, shape, FULL, color, selected );
470 for ( int i = x + l - 1; i > x; i-- )
472 draw_shape( i, y, shape, CONTINUED, 0, selected );
476 /** draw a vertical line with flags */
478 Canvas::draw_line ( int x, int flags )
480 for ( uint y = m.vp->h; y-- ; )
481 m.current[ x ][ y ].flags |= flags;
485 Canvas::playhead_moved ( void )
487 int x = m.grid->ts_to_x( m.grid->index() );
489 return m.playhead != x;
492 /** draw only the playhead--without reexamining the grid */
494 Canvas::draw_playhead ( void )
496 int x = m.grid->ts_to_x( m.grid->index() );
498 if ( m.playhead == x )
503 if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w )
505 if ( config.follow_playhead )
507 m.vp->x = m.playhead / m.vp->w * m.vp->w;
509 m.ruler_drawn = false;
519 for ( uint x = m.vp->w; x-- ; )
520 for ( uint y = m.vp->h; y-- ; )
521 m.current[ x ][ y ].flags &= ~ (F_PLAYHEAD | F_P1 | F_P2 );
525 /* actually if we're recording, we should draw the grid once per
526 * playhead movement also */
527 if ( pattern::recording() == m.grid )
535 /** draw ONLY those nodes necessary to bring the canvas up-to-date with the grid */
537 Canvas::draw ( void )
539 DMESSAGE( "drawing canvas" );
546 m.grid->draw( this, m.vp->x, m.vp->y, m.vp->w, m.vp->h );
549 /** redraw every node on the canvas from the buffer (without
550 * necessarily reexamining the grid) */
552 Canvas::redraw ( void )
554 DMESSAGE( "redrawing canvas" );
556 if ( ! m.grid_drawn )
559 m.ruler_drawn = false;
560 m.mapping_drawn = false;
565 const int shape = m.grid->draw_shape();
567 for ( int y = m.vp->h; y--; )
568 for ( int x = m.vp->w; x--; )
570 cell_t c = m.previous[ x ][ y ];
572 if ( m.vp->x + x == m.playhead )
573 c.flags |= F_PLAYHEAD;
575 gui_draw_shape( m.origin_x + m.margin_left + x * m.div_w, m.origin_y + m.margin_top + y * m.div_h, m.div_w, m.div_h,
576 shape, c.state, c.flags, c.color );
580 /** convert pixel coords into grid coords. returns true if valid */
582 Canvas::grid_pos ( int *x, int *y ) const
584 *y = (*y - m.margin_top - m.origin_y) / m.div_h;
585 *x = (*x - m.margin_left - m.origin_x) / m.div_w;
587 if ( *x < 0 || *y < 0 || *x >= m.vp->w || *y >= m.vp->h )
590 /* adjust for viewport */
594 /* adjust for row-compaction */
606 /* These methods translate viewport pixel coords to absolute grid
607 coords and pass on to the grid. */
609 /** if coords correspond to a row name entry, return the (absolute) note number, otherwise return -1 */
611 Canvas::is_row_name ( int x, int y )
613 if ( x - m.origin_x >= m.margin_left )
620 return m.grid->y_to_note( y );
624 Canvas::start_cursor ( int x, int y )
626 if ( ! grid_pos( &x, &y ) )
629 m.ruler_drawn = false;
640 Canvas::end_cursor ( int x, int y )
642 if ( ! grid_pos( &x, &y ) )
645 m.ruler_drawn = false;
656 Canvas::set ( int x, int y )
658 if ( y - m.origin_y < m.margin_top )
659 /* looks like a click on the ruler */
661 if ( x - m.margin_left - m.origin_x >= 0 )
663 m.p1 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w);
664 m.ruler_drawn = false;
676 if ( ! grid_pos( &x, &y ) )
679 m.grid->put( x, y, 0 );
683 Canvas::unset ( int x, int y )
685 if ( y - m.origin_y < m.margin_top )
686 /* looks like a click on the ruler */
688 if ( x - m.margin_left - m.origin_x >= 0 )
690 m.p2 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w);
691 m.ruler_drawn = false;
703 if ( ! grid_pos( &x, &y ) )
710 Canvas::adj_color ( int x, int y, int n )
712 if ( ! grid_pos( &x, &y ) )
715 m.grid->adj_velocity( x, y, n );
719 Canvas::adj_length ( int x, int y, int n )
721 if ( ! grid_pos( &x, &y ) )
724 m.grid->adj_duration( x, y, n );
728 Canvas::select ( int x, int y )
730 if ( ! grid_pos( &x, &y ) )
733 m.grid->toggle_select( x, y );
737 Canvas::move_selected ( int dir, int n )
742 m.grid->move_selected( n );
745 m.grid->move_selected( 0 - n );
750 /* row-compaction makes this a little complicated */
751 event_list *el = m.grid->events();
753 /* FIXME: don't allow movement beyond the edges! */
757 /* m.grid->selected_hi_lo_note( &hi, &lo ); */
759 /* hi = ntr( hi ) > 0 ? ntr( hi ) : */
761 /* if ( m.grid->y_to_note( ntr( hi ) ) ) */
765 for ( int y = 0; y <= m.maxh; ++y )
766 el->rewrite_selected( m.grid->y_to_note( rtn( y ) ), m.grid->y_to_note( rtn( y - n ) ) );
768 for ( int y = m.maxh; y >= 0; --y )
769 el->rewrite_selected( m.grid->y_to_note( rtn( y ) ), m.grid->y_to_note( rtn( y + n ) ) );
771 m.grid->events( el );
780 Canvas::randomize_row ( int y )
782 int x = m.margin_left;
784 if ( ! grid_pos( &x, &y ) )
787 ((pattern*)m.grid)->randomize_row( y, song.random.feel, song.random.probability );
811 Canvas::select_range ( void )
814 m.grid->select( m.p1, m.p2 );
816 m.grid->select( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
820 Canvas::invert_selection ( void )
822 m.grid->invert_selection();
826 Canvas::crop ( void )
829 m.grid->crop( m.p1, m.p2 );
831 m.grid->crop( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
838 m.ruler_drawn = false;
842 Canvas::delete_time ( void )
844 m.grid->delete_time( m.p1, m.p2 );
849 Canvas::insert_time ( void )
851 m.grid->insert_time( m.p1, m.p2 );
854 /** paste range as new grid */
856 Canvas::duplicate_range ( void )
858 Grid *g = m.grid->clone();
860 g->crop( m.p1, m.p2 );
865 Canvas::row_compact ( int n )
870 m.row_compact = false;
874 m.row_compact = true;
876 _update_row_mapping();
879 row_compact( m.row_compact ? OFF : ON );
883 m.mapping_drawn = false;
887 Canvas::pan ( int dir, int n )
892 case LEFT: case RIGHT: case TO_PLAYHEAD: case TO_NEXT_NOTE: case TO_PREV_NOTE:
893 /* handle horizontal movement specially */
894 n *= m.grid->division();
895 m.ruler_drawn = false;
899 m.mapping_drawn = false;
906 m.vp->x = max( m.vp->x - n, 0 );
912 m.vp->x = m.playhead - (m.playhead % m.grid->division());
915 m.vp->y = max( m.vp->y - n, 0 );
918 m.vp->y = min( m.vp->y + n, m.maxh - m.vp->h );
922 int x = m.grid->next_note_x( m.vp->x );
923 m.vp->x = x - (x % m.grid->division() );
928 int x = m.grid->prev_note_x( m.vp->x );
929 m.vp->x = x - (x % m.grid->division() );
939 Canvas::can_scroll ( int *left, int *right, int *up, int *down )
944 *down = m.maxh - ( m.vp->y + m.vp->h );
948 /** adjust horizontal zoom (* n) */
950 Canvas::h_zoom ( float n )
952 m.vp->w = max( 32, min( (int)(m.vp->w * n), 256 ) );
960 Canvas::v_zoom_fit ( void )
976 /** adjust vertical zoom (* n) */
978 Canvas::v_zoom ( float n )
980 m.vp->h = max( 1, min( (int)(m.vp->h * n), m.maxh ) );
988 Canvas::notes ( char *s )
994 Canvas::notes ( void )
996 return m.grid->notes();