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.

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