Update NTK.
[nondaw.git] / sequencer / src / canvas.C
blobe9a9a57d7bf07b41cb017aab6dde9f4190a95267
2 /*******************************************************************************/
3 /* Copyright (C) 2007-2008 Jonathan Moore Liles                                */
4 /*                                                                             */
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.                                                  */
9 /*                                                                             */
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   */
13 /* more details.                                                               */
14 /*                                                                             */
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 */
24 #include "canvas.H"
25 #include "pattern.H"
26 #include "gui/draw.H"
27 #include "common.h"
29 #include "non.H"
31 cell_t **
32 Canvas::_alloc_array ( void )
34     cell_t **a;
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 );
41     m.size = one + two;
43     cell_t *c = (cell_t *) (((unsigned char *)a) + one);
45     for ( uint x = m.vp->w; x-- ; )
46     {
47         a[x] = c;
48         c += m.vp->h;
49         for ( uint y = m.vp->h; y-- ; )
50         {
51             a[ x ][ y ].flags = 0;
52             a[ x ][ y ].state = -1;
53             a[ x ][ y ].color = 0;
54         }
55     }
57     m.w = m.vp->w;
58     m.h = m.vp->h;
60     return a;
63 Canvas::Canvas ( )
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;
69     m.draw = false;
70     m.ruler_drawn = false;
71     m.mapping_drawn = false;
72     m.grid_drawn = false;
74     m.current = m.previous = NULL;
76     m.row_compact = true;
78     m.maxh = 128;
80     m.vp = NULL;
83 void
84 Canvas::handle_event_change ( void )
86     /* mark the song as dirty and pass the signal on */
87     song.set_dirty();
89     signal_draw();
92 /** change grid to /g/, returns TRUE if new grid size differs from old */
93 void
94 Canvas::grid ( Grid *g )
96     m.grid = g;
98     if ( ! g )
99         return;
101     m.vp = &g->viewport;
103     char *s = m.vp->dump();
104     DMESSAGE( "viewport: %s", s );
105     free( s );
107     m.ruler_drawn = false;
109     resize_grid();
111     update_mapping();
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() );
120     signal_draw();
121     signal_settings_change();
122     signal_pan();
125 /** keep row compaction tables up-to-date */
126 void
127 Canvas::_update_row_mapping ( void )
129     /* reset */
130     for ( int i = 128; i-- ; )
131         m.rtn[i] = m.ntr[i] = -1;
133     DMESSAGE( "updating row mapping" );
135     /* rebuild */
136     int r = 0;
137     for ( int n = 0; n < 128; ++n )
138     {
139         if ( m.grid->row_name( n ) )
140         {
141             m.rtn[r] = n;
142             m.ntr[n] = r;
143             ++r;
144         }
145     }
147     if ( m.row_compact && r )
148         m.maxh = r;
149     else
150         m.maxh = 128;
152     m.vp->h = min( m.vp->h, m.maxh );
155 /** update everything about mapping, leaving the viewport alone */
156 void
157 Canvas::update_mapping ( void )
159     _update_row_mapping();
161     m.mapping_drawn = false;
163     resize();
165     int old_margin = m.margin_left;
167     m.margin_left = 0;
169     m.draw = false;
171     m.grid->draw_row_names( this );
173     m.draw = true;
175     if ( m.margin_left != old_margin )
176     {
177         signal_resize();
178         signal_draw();
179     }
180     else
181         signal_draw();
185 /** change grid mapping */
186 void
187 Canvas::changed_mapping ( void )
189     update_mapping();
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);
196     signal_pan();
199 Grid *
200 Canvas::grid ( void )
202     return m.grid;
206 /** recalculate node sizes based on physical dimensions */
207 void
208 Canvas::resize ( void )
210     if ( ! m.vp )
211         return;
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 */
220 void
221 Canvas::resize_grid ( void )
223     //   _update_row_mapping();
225     resize();
227     if ( m.vp )
228     {
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 )
231         {
232             if ( m.grid_drawn )
233                 signal_resize();
235             m.old_div_w = m.div_w;
236             m.old_div_h = m.div_h;
237         }
238         else
239             return;
240     }
242     DMESSAGE( "resizing grid %dx%d", m.vp->w, m.vp->h );
244     if ( m.previous )
245     {
246         free( m.previous );
247         free( m.current );
248     }
250     m.current = _alloc_array();
251     m.previous = _alloc_array();
253     m.grid_drawn = false;
256 /** inform the canvas with new phsyical dimensions */
257 void
258 Canvas::resize ( int x, int y, int w, int h )
260     m.origin_x = x;
261     m.origin_y = y;
263     m.width = w;
264     m.height = h;
266     resize();
271 /***********/
272 /* Drawing */
273 /***********/
275 /** copy last buffer into current */
276 void
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 */
286 void
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") */
297 void
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--; )
306         {
307             m.current[ x ][ y ].color = 0;
308             m.current[ x ][ y ].state = EMPTY;
309             m.current[ x ][ y ].flags = 0;
310         }
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? */
325 bool
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 */
332 void
333 Canvas::flip ( void )
335     /* FIXME: should this not go in clear()? */
336     if ( m.p1 != m.p2 )
337     {
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 );
340     }
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--; )
348         {
349             cell_t *c = &m.current[ x ][ y ];
350             cell_t *p = &m.previous[ x ][ y ];
352             /* draw selection rect */
353             if ( m.p3 != m.p4 )
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;
358             if ( *c != *p )
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 );
361         }
363     cell_t **tmp = m.previous;
365     m.previous = m.current;
366     m.current = tmp;
369 /** redraw the ruler at the top of the canvas */
370 void
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  */
379 void
380 Canvas::draw_row_name ( int y, const char *name, int color )
382     bool draw = m.draw;
383     bool clear = false;
385     y = ntr( y );
387     if ( ! m.row_compact && ! name )
388         clear = true;
390     y -= m.vp->y;
392     int bx = m.origin_x;
393     int by = m.origin_y + m.margin_top + y * m.div_h;
394     int bw = m.margin_left;
395     int bh = m.div_h;
397     if ( y < 0 || y >= m.vp->h )
398         draw = false;
400     if ( clear && draw )
401         gui_clear_area( bx, by, bw, bh );
402     else
403         m.margin_left = max( m.margin_left, gui_draw_string( bx, by,
404                                                              bw, bh,
405                                                              color,
406                                                              name,
407                                                              draw ) );
410 /** redraw row names */
411 void
412 Canvas::redraw_mapping ( void )
414     m.margin_left = 0;
416     m.draw = false;
418     m.grid->draw_row_names( this );
420     resize();
422     m.draw = true;
424     m.grid->draw_row_names( this );
426     m.mapping_drawn = true;
429 void
430 Canvas::draw_mapping ( void )
432     if ( ! m.mapping_drawn ) redraw_mapping();
435 void
436 Canvas::draw_ruler ( void )
438     if ( ! m.ruler_drawn ) redraw_ruler();
441 /** "draw" a shape in the backbuffer */
442 void
443 Canvas::draw_shape ( int x, int y, int shape, int state, int color, bool selected )
445     y = ntr( y );
447     if ( y < 0 )
448         return;
450     // adjust for viewport.
452     x -= m.vp->x;
453     y -= m.vp->y;
455     if ( x < 0 || y < 0 || x >= m.vp->w || y >= m.vp->h )
456         return;
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;
460     if ( selected )
461         m.current[ x ][ y ].state = SELECTED;
462     m.current[ x ][ y ].flags = 0;
465 /** callback used by Grid::draw()  */
466 void
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-- )
471     {
472         draw_shape( i, y, shape, CONTINUED, 0, selected );
473     }
476 /** draw a vertical line with flags */
477 void
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() );
488     
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 )
499         return 0;
501     m.playhead = x;
503     if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w )
504     {
505         if ( config.follow_playhead )
506         {
507             m.vp->x = m.playhead / m.vp->w * m.vp->w;
509             m.ruler_drawn = false;
511             signal_draw();
513             return 0;
514         }
515     }
517     copy();
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 );
523     flip();
525     /* actually if we're recording, we should draw the grid once per
526      * playhead movement also */
527     if ( pattern::recording() == m.grid )
528     {
529         draw();
530     }
532     return 1;
535 /** draw ONLY those nodes necessary to bring the canvas up-to-date with the grid */
536 void
537 Canvas::draw ( void )
539     DMESSAGE( "drawing canvas" );
541     draw_mapping();
542     draw_ruler();
544     m.grid_drawn = true;
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) */
551 void
552 Canvas::redraw ( void )
554     DMESSAGE( "redrawing canvas" );
556     if ( ! m.grid_drawn )
557         draw();
559     m.ruler_drawn = false;
560     m.mapping_drawn = false;
562     draw_mapping();
563     draw_ruler();
565     const int shape = m.grid->draw_shape();
567     for ( int y = m.vp->h; y--; )
568         for ( int x = m.vp->w; x--; )
569         {
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 );
577         }
580 /** convert pixel coords into grid coords. returns true if valid */
581 bool
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 )
588         return false;
590     /* adjust for viewport */
591     *x += m.vp->x;
592     *y += m.vp->y;
594     /* adjust for row-compaction */
595     *y = rtn( *y );
597     return true;
602 /******************/
603 /* Input handlers */
604 /******************/
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 )
614         return -1;
616     x = m.margin_left;
618     grid_pos( &x, &y );
620     return m.grid->y_to_note( y );
623 void
624 Canvas::start_cursor ( int x, int y )
626     if ( ! grid_pos( &x, &y ) )
627         return;
629     m.ruler_drawn = false;
631     m.p1 = x;
632     m.p3 = ntr( y );
634     _lr();
636     signal_draw();
639 void
640 Canvas::end_cursor ( int x, int y )
642     if ( ! grid_pos( &x, &y ) )
643         return;
645     m.ruler_drawn = false;
647     m.p2 = x;
648     m.p4 = ntr( y );
650     _lr();
652     signal_draw();
655 void
656 Canvas::set ( int x, int y )
658     if ( y - m.origin_y < m.margin_top )
659         /* looks like a click on the ruler */
660     {
661         if ( x - m.margin_left - m.origin_x >= 0 )
662         {
663             m.p1 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w);
664             m.ruler_drawn = false;
666             m.p3 = m.p4 = 0;
667         }
669         _lr();
671         signal_draw();
673         return;
674     }
676     if ( ! grid_pos( &x, &y ) )
677         return;
679     m.grid->put( x, y, 0 );
682 void
683 Canvas::unset ( int x, int y )
685     if ( y - m.origin_y < m.margin_top )
686         /* looks like a click on the ruler */
687     {
688         if ( x - m.margin_left - m.origin_x >= 0 )
689         {
690             m.p2 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w);
691             m.ruler_drawn = false;
693             m.p3 = m.p4 = 0;
694         }
696         _lr();
698         signal_draw();
700         return;
701     }
703     if ( ! grid_pos( &x, &y ) )
704         return;
706     m.grid->del( x, y );
709 void
710 Canvas::adj_color ( int x, int y, int n )
712     if ( ! grid_pos( &x, &y ) )
713         return;
715     m.grid->adj_velocity( x, y, n );
718 void
719 Canvas::adj_length ( int x, int y, int n )
721     if ( ! grid_pos( &x, &y ) )
722         return;
724     m.grid->adj_duration( x, y, n );
727 void
728 Canvas::select ( int x, int y )
730     if ( ! grid_pos( &x, &y ) )
731         return;
733     m.grid->toggle_select( x, y );
736 void
737 Canvas::move_selected ( int dir, int n )
739     switch ( dir )
740     {
741         case RIGHT:
742             m.grid->move_selected( n );
743             break;
744         case LEFT:
745             m.grid->move_selected( 0 - n );
746             break;
747         case UP:
748         case DOWN:
749         {
750             /* row-compaction makes this a little complicated */
751             event_list *el = m.grid->events();
753             /* FIXME: don't allow movement beyond the edges!  */
755 /*             int hi, lo; */
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 ) ) ) */
764             if ( dir == UP )
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 ) ) );
767             else
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 );
773             delete el;
774             break;
775         }
776     }
779 void
780 Canvas::randomize_row ( int y )
782     int x = m.margin_left;
784     if ( ! grid_pos( &x, &y ) )
785         return;
787     ((pattern*)m.grid)->randomize_row( y, song.random.feel, song.random.probability );
790 void
791 Canvas::_lr ( void )
793     int l, r;
795     if ( m.p2 > m.p1 )
796     {
797         l = m.p1;
798         r = m.p2;
799     }
800     else
801     {
802         l = m.p2;
803         r = m.p1;
804     }
806     m.p1 = l;
807     m.p2 = r;
810 void
811 Canvas::select_range ( void )
813     if ( m.p3 == m.p4 )
814         m.grid->select( m.p1, m.p2 );
815     else
816         m.grid->select( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
819 void
820 Canvas::invert_selection ( void )
822     m.grid->invert_selection();
825 void
826 Canvas::crop ( void )
828     if ( m.p3 == m.p4 )
829         m.grid->crop( m.p1, m.p2 );
830     else
831         m.grid->crop( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
833     m.vp->x = 0;
835     m.p2 = m.p2 - m.p1;
836     m.p1 = 0;
838     m.ruler_drawn = false;
841 void
842 Canvas::delete_time ( void )
844     m.grid->delete_time( m.p1, m.p2 );
848 void
849 Canvas::insert_time ( void )
851     m.grid->insert_time( m.p1, m.p2 );
854 /** paste range as new grid */
855 void
856 Canvas::duplicate_range ( void )
858     Grid *g = m.grid->clone();
860     g->crop( m.p1, m.p2 );
861     g->viewport.x = 0;
864 void
865 Canvas::row_compact ( int n )
867     switch ( n )
868     {
869         case OFF:
870             m.row_compact = false;
871             m.maxh = 128;
872             break;
873         case ON:
874             m.row_compact = true;
875             m.vp->y = 0;
876             _update_row_mapping();
877             break;
878         case TOGGLE:
879             row_compact( m.row_compact ? OFF : ON );
880             break;
881     }
882     _reset();
883     m.mapping_drawn = false;
886 void
887 Canvas::pan ( int dir, int n )
890     switch ( dir )
891     {
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;
896             break;
897         default:
898             n *= 5;
899             m.mapping_drawn = false;
900             break;
901     }
903     switch ( dir )
904     {
905         case LEFT:
906             m.vp->x = max( m.vp->x - n, 0 );
907             break;
908         case RIGHT:
909             m.vp->x += n;
910             break;
911         case TO_PLAYHEAD:
912             m.vp->x = m.playhead - (m.playhead % m.grid->division());
913             break;
914         case UP:
915             m.vp->y = max( m.vp->y - n, 0 );
916             break;
917         case DOWN:
918             m.vp->y = min( m.vp->y + n, m.maxh - m.vp->h );
919             break;
920         case TO_NEXT_NOTE:
921         {
922             int x = m.grid->next_note_x( m.vp->x );
923             m.vp->x = x - (x % m.grid->division() );
924             break;
925         }
926         case TO_PREV_NOTE:
927         {
928             int x = m.grid->prev_note_x( m.vp->x );
929             m.vp->x = x - (x % m.grid->division() );
930             break;
931         }
932     }
934     signal_draw();
935     signal_pan();
938 void
939 Canvas::can_scroll ( int *left, int *right, int *up, int *down )
941     *left = m.vp->x;
942     *right = -1;
943     *up = m.vp->y;
944     *down = m.maxh - ( m.vp->y + m.vp->h );
948 /** adjust horizontal zoom (* n) */
949 void
950 Canvas::h_zoom ( float n )
952     m.vp->w = max( 32, min( (int)(m.vp->w * n), 256 ) );
954     resize_grid();
956     song.set_dirty();
959 void
960 Canvas::v_zoom_fit ( void )
962     if ( ! m.grid )
963         return;
965     changed_mapping();
967     m.vp->h = m.maxh;
968     m.vp->y = 0;
970     resize_grid();
972     song.set_dirty();
976 /** adjust vertical zoom (* n) */
977 void
978 Canvas::v_zoom ( float n )
980     m.vp->h = max( 1, min( (int)(m.vp->h * n), m.maxh ) );
982     resize_grid();
984     song.set_dirty();
987 void
988 Canvas::notes ( char *s )
990     m.grid->notes( s );
993 char *
994 Canvas::notes ( void )
996     return m.grid->notes();