Assists music production by grouping standalone programs into sessions. Community version of "Non Session Manager".
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

998 lines
20KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2007-2008 Jonathan Moore Liles */
  3. /* */
  4. /* This program is free software; you can redistribute it and/or modify it */
  5. /* under the terms of the GNU General Public License as published by the */
  6. /* Free Software Foundation; either version 2 of the License, or (at your */
  7. /* option) any later version. */
  8. /* */
  9. /* This program is distributed in the hope that it will be useful, but WITHOUT */
  10. /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
  11. /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
  12. /* more details. */
  13. /* */
  14. /* You should have received a copy of the GNU General Public License along */
  15. /* with This program; see the file COPYING. If not,write to the Free Software */
  16. /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /*******************************************************************************/
  18. /* This is a generic double-buffering, optimizing canvas interface to
  19. grids (patterns and phrases). It draws only what is necessary to keep
  20. the display up-to-date. Actual drawing functions are in draw.C */
  21. #include "canvas.H"
  22. #include "pattern.H"
  23. #include "gui/draw.H"
  24. #include "common.h"
  25. #include "non.H"
  26. cell_t **
  27. Canvas::_alloc_array ( void )
  28. {
  29. cell_t **a;
  30. int one = sizeof( typeof( a ) ) * m.vp->w;
  31. int two = sizeof( typeof( a[0] ) ) * (m.vp->h * m.vp->w);
  32. a = (cell_t **) malloc( one + two );
  33. m.size = one + two;
  34. cell_t *c = (cell_t *) (((unsigned char *)a) + one);
  35. for ( uint x = m.vp->w; x-- ; )
  36. {
  37. a[x] = c;
  38. c += m.vp->h;
  39. for ( uint y = m.vp->h; y-- ; )
  40. {
  41. a[ x ][ y ].flags = 0;
  42. a[ x ][ y ].state = -1;
  43. a[ x ][ y ].color = 0;
  44. }
  45. }
  46. m.w = m.vp->w;
  47. m.h = m.vp->h;
  48. return a;
  49. }
  50. Canvas::Canvas ( )
  51. {
  52. 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;
  53. m.margin_top = ruler_height;
  54. m.draw = false;
  55. m.ruler_drawn = false;
  56. m.mapping_drawn = false;
  57. m.grid_drawn = false;
  58. m.current = m.previous = NULL;
  59. m.row_compact = true;
  60. m.maxh = 128;
  61. m.vp = NULL;
  62. }
  63. void
  64. Canvas::handle_event_change ( void )
  65. {
  66. /* mark the song as dirty and pass the signal on */
  67. song.set_dirty();
  68. signal_draw();
  69. }
  70. /** change grid to /g/, returns TRUE if new grid size differs from old */
  71. void
  72. Canvas::grid ( Grid *g )
  73. {
  74. m.grid = g;
  75. if ( ! g )
  76. return;
  77. m.vp = &g->viewport;
  78. char *s = m.vp->dump();
  79. DMESSAGE( "viewport: %s", s );
  80. free( s );
  81. m.ruler_drawn = false;
  82. resize_grid();
  83. update_mapping();
  84. // m.shape = m.grid->draw_shape();
  85. /* connect signals */
  86. /* FIXME: what happens when we do this twice? */
  87. g->signal_events_change.connect( mem_fun( this, &Canvas::handle_event_change ) );
  88. g->signal_settings_change.connect( signal_settings_change.make_slot() );
  89. signal_draw();
  90. signal_settings_change();
  91. signal_pan();
  92. }
  93. /** keep row compaction tables up-to-date */
  94. void
  95. Canvas::_update_row_mapping ( void )
  96. {
  97. /* reset */
  98. for ( int i = 128; i-- ; )
  99. m.rtn[i] = m.ntr[i] = -1;
  100. DMESSAGE( "updating row mapping" );
  101. /* rebuild */
  102. int r = 0;
  103. for ( int n = 0; n < 128; ++n )
  104. {
  105. if ( m.grid->row_name( n ) )
  106. {
  107. m.rtn[r] = n;
  108. m.ntr[n] = r;
  109. ++r;
  110. }
  111. }
  112. if ( m.row_compact && r )
  113. m.maxh = r;
  114. else
  115. m.maxh = 128;
  116. m.vp->h = min( m.vp->h, m.maxh );
  117. }
  118. /** update everything about mapping, leaving the viewport alone */
  119. void
  120. Canvas::update_mapping ( void )
  121. {
  122. _update_row_mapping();
  123. m.mapping_drawn = false;
  124. resize();
  125. int old_margin = m.margin_left;
  126. m.margin_left = 0;
  127. m.draw = false;
  128. m.grid->draw_row_names( this );
  129. m.draw = true;
  130. if ( m.margin_left != old_margin )
  131. {
  132. signal_resize();
  133. signal_draw();
  134. }
  135. else
  136. signal_draw();
  137. }
  138. /** change grid mapping */
  139. void
  140. Canvas::changed_mapping ( void )
  141. {
  142. update_mapping();
  143. m.vp->h = min( m.vp->h, m.maxh );
  144. if ( m.vp->y + m.vp->h > m.maxh )
  145. m.vp->y = (m.maxh / 2) - (m.vp->h / 2);
  146. signal_pan();
  147. }
  148. Grid *
  149. Canvas::grid ( void )
  150. {
  151. return m.grid;
  152. }
  153. /** recalculate node sizes based on physical dimensions */
  154. void
  155. Canvas::resize ( void )
  156. {
  157. if ( ! m.vp )
  158. return;
  159. m.div_w = (m.width - m.margin_left) / m.vp->w;
  160. m.div_h = (m.height - m.margin_top) / m.vp->h;
  161. m.mapping_drawn = m.ruler_drawn = false;
  162. }
  163. /** reallocate buffers to match grid dimensions */
  164. void
  165. Canvas::resize_grid ( void )
  166. {
  167. // _update_row_mapping();
  168. resize();
  169. if ( m.vp )
  170. {
  171. if ( m.vp->w != m.w || m.vp->h != m.h ||
  172. m.div_w != m.old_div_w || m.div_h != m.old_div_h )
  173. {
  174. if ( m.grid_drawn )
  175. signal_resize();
  176. m.old_div_w = m.div_w;
  177. m.old_div_h = m.div_h;
  178. }
  179. else
  180. return;
  181. }
  182. DMESSAGE( "resizing grid %dx%d", m.vp->w, m.vp->h );
  183. if ( m.previous )
  184. {
  185. free( m.previous );
  186. free( m.current );
  187. }
  188. m.current = _alloc_array();
  189. m.previous = _alloc_array();
  190. m.grid_drawn = false;
  191. }
  192. /** inform the canvas with new phsyical dimensions */
  193. void
  194. Canvas::resize ( int x, int y, int w, int h )
  195. {
  196. m.origin_x = x;
  197. m.origin_y = y;
  198. m.width = w;
  199. m.height = h;
  200. resize();
  201. }
  202. /***********/
  203. /* Drawing */
  204. /***********/
  205. /** copy last buffer into current */
  206. void
  207. Canvas::copy ( void )
  208. {
  209. for ( uint y = m.vp->h; y-- ; )
  210. for ( uint x = m.vp->w; x-- ; )
  211. m.current[ x ][ y ] = m.previous[ x ][ y ];
  212. }
  213. /** reset last buffer */
  214. void
  215. Canvas::_reset ( void )
  216. {
  217. cell_t empty = {0,0,0};
  218. for ( uint y = m.vp->h; y-- ; )
  219. for ( uint x = m.vp->w; x-- ; )
  220. m.current[ x ][ y ] = empty;
  221. }
  222. /** prepare current buffer for drawing (draw "background") */
  223. void
  224. Canvas::clear ( void )
  225. {
  226. uint rule = m.grid->ppqn();
  227. uint lx = m.grid->ts_to_x( m.grid->length() );
  228. for ( uint y = m.vp->h; y--; )
  229. for ( uint x = m.vp->w; x--; )
  230. {
  231. m.current[ x ][ y ].color = 0;
  232. m.current[ x ][ y ].state = EMPTY;
  233. m.current[ x ][ y ].flags = 0;
  234. }
  235. for ( int x = m.vp->w - rule; x >= 0; x -= rule )
  236. for ( uint y = m.vp->h; y-- ; )
  237. m.current[ x ][ y ].state = LINE;
  238. int sx = (int)(lx - m.vp->x) >= 0 ? lx - m.vp->x : 0;
  239. for ( int x = sx; x < m.vp->w; ++x )
  240. for ( int y = m.vp->h; y-- ; )
  241. m.current[ x ][ y ].state = PARTIAL;
  242. }
  243. /** is /x/ within the viewport? */
  244. bool
  245. Canvas::viewable_x ( int x )
  246. {
  247. return x >= m.vp->x && x < m.vp->x + m.vp->w;
  248. }
  249. /** flush delta of last and current buffers to screen, then flip them */
  250. void
  251. Canvas::flip ( void )
  252. {
  253. /* FIXME: should this not go in clear()? */
  254. if ( m.p1 != m.p2 )
  255. {
  256. if ( viewable_x( m.p1 ) ) draw_line( m.p1 - m.vp->x, F_P1 );
  257. if ( viewable_x( m.p2 ) ) draw_line( m.p2 - m.vp->x, F_P2 );
  258. }
  259. if ( viewable_x( m.playhead ) ) draw_line( m.playhead - m.vp->x, F_PLAYHEAD );
  260. const int shape = m.grid->draw_shape();
  261. for ( uint y = m.vp->h; y--; )
  262. for ( uint x = m.vp->w; x--; )
  263. {
  264. cell_t *c = &m.current[ x ][ y ];
  265. cell_t *p = &m.previous[ x ][ y ];
  266. /* draw selection rect */
  267. if ( m.p3 != m.p4 )
  268. if ( y + m.vp->y >= m.p3 && x + m.vp->x >= m.p1 &&
  269. y + m.vp->y <= m.p4 && x + m.vp->x < m.p2 )
  270. c->flags |= F_SELECTION;
  271. if ( *c != *p )
  272. 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,
  273. shape, c->state, c->flags, c->color );
  274. }
  275. cell_t **tmp = m.previous;
  276. m.previous = m.current;
  277. m.current = tmp;
  278. }
  279. /** redraw the ruler at the top of the canvas */
  280. void
  281. Canvas::redraw_ruler ( void )
  282. {
  283. 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,
  284. m.p1 - m.vp->x, m.p2 - m.vp->x );
  285. m.ruler_drawn = true;
  286. }
  287. /** callback called by Grid::draw_row_names() to draw an individual row name */
  288. void
  289. Canvas::draw_row_name ( int y, const char *name, int color )
  290. {
  291. bool draw = m.draw;
  292. bool clear = false;
  293. y = ntr( y );
  294. if ( ! m.row_compact && ! name )
  295. clear = true;
  296. y -= m.vp->y;
  297. int bx = m.origin_x;
  298. int by = m.origin_y + m.margin_top + y * m.div_h;
  299. int bw = m.margin_left;
  300. int bh = m.div_h;
  301. if ( y < 0 || y >= m.vp->h )
  302. draw = false;
  303. if ( clear && draw )
  304. gui_clear_area( bx, by, bw, bh );
  305. else
  306. m.margin_left = max( m.margin_left, gui_draw_string( bx, by,
  307. bw, bh,
  308. color,
  309. name,
  310. draw ) );
  311. }
  312. /** redraw row names */
  313. void
  314. Canvas::redraw_mapping ( void )
  315. {
  316. m.margin_left = 0;
  317. m.draw = false;
  318. m.grid->draw_row_names( this );
  319. resize();
  320. m.draw = true;
  321. m.grid->draw_row_names( this );
  322. m.mapping_drawn = true;
  323. }
  324. void
  325. Canvas::draw_mapping ( void )
  326. {
  327. if ( ! m.mapping_drawn ) redraw_mapping();
  328. }
  329. void
  330. Canvas::draw_ruler ( void )
  331. {
  332. if ( ! m.ruler_drawn ) redraw_ruler();
  333. }
  334. /** "draw" a shape in the backbuffer */
  335. void
  336. Canvas::draw_shape ( int x, int y, int shape, int state, int color, bool selected )
  337. {
  338. y = ntr( y );
  339. if ( y < 0 )
  340. return;
  341. // adjust for viewport.
  342. x -= m.vp->x;
  343. y -= m.vp->y;
  344. if ( x < 0 || y < 0 || x >= m.vp->w || y >= m.vp->h )
  345. return;
  346. m.current[ x ][ y ].color = color;
  347. m.current[ x ][ y ].state = (uint)m.vp->x + x > m.grid->ts_to_x( m.grid->length() ) ? PARTIAL : state;
  348. if ( selected )
  349. m.current[ x ][ y ].state = SELECTED;
  350. m.current[ x ][ y ].flags = 0;
  351. }
  352. /** callback used by Grid::draw() */
  353. void
  354. Canvas::draw_dash ( int x, int y, int l, int shape, int color, bool selected )
  355. {
  356. draw_shape( x, y, shape, FULL, color, selected );
  357. for ( int i = x + l - 1; i > x; i-- )
  358. {
  359. draw_shape( i, y, shape, CONTINUED, 0, selected );
  360. }
  361. }
  362. /** draw a vertical line with flags */
  363. void
  364. Canvas::draw_line ( int x, int flags )
  365. {
  366. for ( uint y = m.vp->h; y-- ; )
  367. m.current[ x ][ y ].flags |= flags;
  368. }
  369. int
  370. Canvas::playhead_moved ( void )
  371. {
  372. int x = m.grid->ts_to_x( m.grid->index() );
  373. return m.playhead != x;
  374. }
  375. /** draw only the playhead--without reexamining the grid */
  376. int
  377. Canvas::draw_playhead ( void )
  378. {
  379. int x = m.grid->ts_to_x( m.grid->index() );
  380. if ( m.playhead == x )
  381. return 0;
  382. m.playhead = x;
  383. if ( m.playhead < m.vp->x || m.playhead >= m.vp->x + m.vp->w )
  384. {
  385. if ( config.follow_playhead )
  386. {
  387. m.vp->x = m.playhead / m.vp->w * m.vp->w;
  388. m.ruler_drawn = false;
  389. signal_draw();
  390. return 0;
  391. }
  392. }
  393. copy();
  394. for ( uint x = m.vp->w; x-- ; )
  395. for ( uint y = m.vp->h; y-- ; )
  396. m.current[ x ][ y ].flags &= ~ (F_PLAYHEAD | F_P1 | F_P2 );
  397. flip();
  398. /* actually if we're recording, we should draw the grid once per
  399. * playhead movement also */
  400. if ( pattern::recording() == m.grid )
  401. {
  402. draw();
  403. }
  404. return 1;
  405. }
  406. /** draw ONLY those nodes necessary to bring the canvas up-to-date with the grid */
  407. void
  408. Canvas::draw ( void )
  409. {
  410. DMESSAGE( "drawing canvas" );
  411. draw_mapping();
  412. draw_ruler();
  413. m.grid_drawn = true;
  414. m.grid->draw( this, m.vp->x, m.vp->y, m.vp->w, m.vp->h );
  415. }
  416. /** redraw every node on the canvas from the buffer (without
  417. * necessarily reexamining the grid) */
  418. void
  419. Canvas::redraw ( void )
  420. {
  421. DMESSAGE( "redrawing canvas" );
  422. if ( ! m.grid_drawn )
  423. draw();
  424. m.ruler_drawn = false;
  425. m.mapping_drawn = false;
  426. draw_mapping();
  427. draw_ruler();
  428. const int shape = m.grid->draw_shape();
  429. for ( int y = m.vp->h; y--; )
  430. for ( int x = m.vp->w; x--; )
  431. {
  432. cell_t c = m.previous[ x ][ y ];
  433. if ( m.vp->x + x == m.playhead )
  434. c.flags |= F_PLAYHEAD;
  435. 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,
  436. shape, c.state, c.flags, c.color );
  437. }
  438. }
  439. /** convert pixel coords into grid coords. returns true if valid */
  440. bool
  441. Canvas::grid_pos ( int *x, int *y ) const
  442. {
  443. *y = (*y - m.margin_top - m.origin_y) / m.div_h;
  444. *x = (*x - m.margin_left - m.origin_x) / m.div_w;
  445. if ( *x < 0 || *y < 0 || *x >= m.vp->w || *y >= m.vp->h )
  446. return false;
  447. /* adjust for viewport */
  448. *x += m.vp->x;
  449. *y += m.vp->y;
  450. /* adjust for row-compaction */
  451. *y = rtn( *y );
  452. return true;
  453. }
  454. /******************/
  455. /* Input handlers */
  456. /******************/
  457. /* These methods translate viewport pixel coords to absolute grid
  458. coords and pass on to the grid. */
  459. /** if coords correspond to a row name entry, return the (absolute) note number, otherwise return -1 */
  460. int
  461. Canvas::is_row_name ( int x, int y )
  462. {
  463. if ( x - m.origin_x >= m.margin_left )
  464. return -1;
  465. x = m.margin_left;
  466. grid_pos( &x, &y );
  467. return m.grid->y_to_note( y );
  468. }
  469. void
  470. Canvas::start_cursor ( int x, int y )
  471. {
  472. if ( ! grid_pos( &x, &y ) )
  473. return;
  474. m.ruler_drawn = false;
  475. m.p1 = x;
  476. m.p3 = ntr( y );
  477. _lr();
  478. signal_draw();
  479. }
  480. void
  481. Canvas::end_cursor ( int x, int y )
  482. {
  483. if ( ! grid_pos( &x, &y ) )
  484. return;
  485. m.ruler_drawn = false;
  486. m.p2 = x;
  487. m.p4 = ntr( y );
  488. _lr();
  489. signal_draw();
  490. }
  491. void
  492. Canvas::set ( int x, int y )
  493. {
  494. if ( y - m.origin_y < m.margin_top )
  495. /* looks like a click on the ruler */
  496. {
  497. if ( x - m.margin_left - m.origin_x >= 0 )
  498. {
  499. m.p1 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w);
  500. m.ruler_drawn = false;
  501. m.p3 = m.p4 = 0;
  502. }
  503. _lr();
  504. signal_draw();
  505. return;
  506. }
  507. if ( ! grid_pos( &x, &y ) )
  508. return;
  509. m.grid->put( x, y, 0 );
  510. }
  511. void
  512. Canvas::unset ( int x, int y )
  513. {
  514. if ( y - m.origin_y < m.margin_top )
  515. /* looks like a click on the ruler */
  516. {
  517. if ( x - m.margin_left - m.origin_x >= 0 )
  518. {
  519. m.p2 = m.vp->x + ((x - m.margin_left - m.origin_x) / m.div_w);
  520. m.ruler_drawn = false;
  521. m.p3 = m.p4 = 0;
  522. }
  523. _lr();
  524. signal_draw();
  525. return;
  526. }
  527. if ( ! grid_pos( &x, &y ) )
  528. return;
  529. m.grid->del( x, y );
  530. }
  531. void
  532. Canvas::adj_color ( int x, int y, int n )
  533. {
  534. if ( ! grid_pos( &x, &y ) )
  535. return;
  536. m.grid->adj_velocity( x, y, n );
  537. }
  538. void
  539. Canvas::adj_length ( int x, int y, int n )
  540. {
  541. if ( ! grid_pos( &x, &y ) )
  542. return;
  543. m.grid->adj_duration( x, y, n );
  544. }
  545. void
  546. Canvas::select ( int x, int y )
  547. {
  548. if ( ! grid_pos( &x, &y ) )
  549. return;
  550. m.grid->toggle_select( x, y );
  551. }
  552. void
  553. Canvas::move_selected ( int dir, int n )
  554. {
  555. switch ( dir )
  556. {
  557. case RIGHT:
  558. m.grid->move_selected( n );
  559. break;
  560. case LEFT:
  561. m.grid->move_selected( 0 - n );
  562. break;
  563. case UP:
  564. case DOWN:
  565. {
  566. /* row-compaction makes this a little complicated */
  567. event_list *el = m.grid->events();
  568. /* FIXME: don't allow movement beyond the edges! */
  569. /* int hi, lo; */
  570. /* m.grid->selected_hi_lo_note( &hi, &lo ); */
  571. /* hi = ntr( hi ) > 0 ? ntr( hi ) : */
  572. /* if ( m.grid->y_to_note( ntr( hi ) ) ) */
  573. if ( dir == UP )
  574. for ( int y = 0; y <= m.maxh; ++y )
  575. el->rewrite_selected( m.grid->y_to_note( rtn( y ) ), m.grid->y_to_note( rtn( y - n ) ) );
  576. else
  577. for ( int y = m.maxh; y >= 0; --y )
  578. el->rewrite_selected( m.grid->y_to_note( rtn( y ) ), m.grid->y_to_note( rtn( y + n ) ) );
  579. m.grid->events( el );
  580. delete el;
  581. break;
  582. }
  583. }
  584. }
  585. void
  586. Canvas::randomize_row ( int y )
  587. {
  588. int x = m.margin_left;
  589. if ( ! grid_pos( &x, &y ) )
  590. return;
  591. ((pattern*)m.grid)->randomize_row( y, song.random.feel, song.random.probability );
  592. }
  593. void
  594. Canvas::_lr ( void )
  595. {
  596. int l, r;
  597. if ( m.p2 > m.p1 )
  598. {
  599. l = m.p1;
  600. r = m.p2;
  601. }
  602. else
  603. {
  604. l = m.p2;
  605. r = m.p1;
  606. }
  607. m.p1 = l;
  608. m.p2 = r;
  609. }
  610. void
  611. Canvas::select_range ( void )
  612. {
  613. if ( m.p3 == m.p4 )
  614. m.grid->select( m.p1, m.p2 );
  615. else
  616. m.grid->select( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
  617. }
  618. void
  619. Canvas::invert_selection ( void )
  620. {
  621. m.grid->invert_selection();
  622. }
  623. void
  624. Canvas::crop ( void )
  625. {
  626. if ( m.p3 == m.p4 )
  627. m.grid->crop( m.p1, m.p2 );
  628. else
  629. m.grid->crop( m.p1, m.p2, rtn( m.p3 ), rtn( m.p4 ) );
  630. m.vp->x = 0;
  631. m.p2 = m.p2 - m.p1;
  632. m.p1 = 0;
  633. m.ruler_drawn = false;
  634. }
  635. void
  636. Canvas::delete_time ( void )
  637. {
  638. m.grid->delete_time( m.p1, m.p2 );
  639. }
  640. void
  641. Canvas::insert_time ( void )
  642. {
  643. m.grid->insert_time( m.p1, m.p2 );
  644. }
  645. /** paste range as new grid */
  646. void
  647. Canvas::duplicate_range ( void )
  648. {
  649. Grid *g = m.grid->clone();
  650. g->crop( m.p1, m.p2 );
  651. g->viewport.x = 0;
  652. }
  653. void
  654. Canvas::row_compact ( int n )
  655. {
  656. switch ( n )
  657. {
  658. case OFF:
  659. m.row_compact = false;
  660. m.maxh = 128;
  661. break;
  662. case ON:
  663. m.row_compact = true;
  664. m.vp->y = 0;
  665. _update_row_mapping();
  666. break;
  667. case TOGGLE:
  668. row_compact( m.row_compact ? OFF : ON );
  669. break;
  670. }
  671. _reset();
  672. m.mapping_drawn = false;
  673. }
  674. void
  675. Canvas::pan ( int dir, int n )
  676. {
  677. switch ( dir )
  678. {
  679. case LEFT: case RIGHT: case TO_PLAYHEAD: case TO_NEXT_NOTE: case TO_PREV_NOTE:
  680. /* handle horizontal movement specially */
  681. n *= m.grid->division();
  682. m.ruler_drawn = false;
  683. break;
  684. default:
  685. n *= 5;
  686. m.mapping_drawn = false;
  687. break;
  688. }
  689. switch ( dir )
  690. {
  691. case LEFT:
  692. m.vp->x = max( m.vp->x - n, 0 );
  693. break;
  694. case RIGHT:
  695. m.vp->x += n;
  696. break;
  697. case TO_PLAYHEAD:
  698. m.vp->x = m.playhead - (m.playhead % m.grid->division());
  699. break;
  700. case UP:
  701. m.vp->y = max( m.vp->y - n, 0 );
  702. break;
  703. case DOWN:
  704. m.vp->y = min( m.vp->y + n, m.maxh - m.vp->h );
  705. break;
  706. case TO_NEXT_NOTE:
  707. {
  708. int x = m.grid->next_note_x( m.vp->x );
  709. m.vp->x = x - (x % m.grid->division() );
  710. break;
  711. }
  712. case TO_PREV_NOTE:
  713. {
  714. int x = m.grid->prev_note_x( m.vp->x );
  715. m.vp->x = x - (x % m.grid->division() );
  716. break;
  717. }
  718. }
  719. signal_draw();
  720. signal_pan();
  721. }
  722. void
  723. Canvas::can_scroll ( int *left, int *right, int *up, int *down )
  724. {
  725. *left = m.vp->x;
  726. *right = -1;
  727. *up = m.vp->y;
  728. *down = m.maxh - ( m.vp->y + m.vp->h );
  729. }
  730. /** adjust horizontal zoom (* n) */
  731. void
  732. Canvas::h_zoom ( float n )
  733. {
  734. m.vp->w = max( 32, min( (int)(m.vp->w * n), 256 ) );
  735. resize_grid();
  736. song.set_dirty();
  737. }
  738. void
  739. Canvas::v_zoom_fit ( void )
  740. {
  741. if ( ! m.grid )
  742. return;
  743. changed_mapping();
  744. m.vp->h = m.maxh;
  745. m.vp->y = 0;
  746. resize_grid();
  747. song.set_dirty();
  748. }
  749. /** adjust vertical zoom (* n) */
  750. void
  751. Canvas::v_zoom ( float n )
  752. {
  753. m.vp->h = max( 1, min( (int)(m.vp->h * n), m.maxh ) );
  754. resize_grid();
  755. song.set_dirty();
  756. }
  757. void
  758. Canvas::notes ( char *s )
  759. {
  760. m.grid->notes( s );
  761. }
  762. char *
  763. Canvas::notes ( void )
  764. {
  765. return m.grid->notes();
  766. }