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.

2150 lines
50KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 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 the Timeline widget, which contains all the tracks and
  19. * provides cursor overlays, scrolling, zooming, measure lines, tempo
  20. * map and just about everything else. */
  21. #include <FL/Fl.H>
  22. #include <FL/Fl_Scroll.H>
  23. #include <FL/Fl_Pack.H>
  24. #include <FL/Fl_Scrollbar.H>
  25. #include <FL/Fl_Widget.H>
  26. #include <FL/fl_draw.H>
  27. #include <FL/Fl_Menu_Button.H>
  28. #include <FL/Fl_Panzoomer.H>
  29. #include "Timeline.H"
  30. #include "Tempo_Sequence.H"
  31. #include "Time_Sequence.H"
  32. #include "Cursor_Sequence.H"
  33. #include "Audio_Sequence.H"
  34. #include "Control_Sequence.H"
  35. #include "Sequence.H"
  36. #include "Annotation_Sequence.H"
  37. #include "Track.H"
  38. #include "Transport.H"
  39. #include "Engine/Engine.H" // for lock()
  40. #include "FL/menu_popup.H"
  41. #include "const.h"
  42. #include "debug.h"
  43. /* these headers are just for the NSM support */
  44. #include "Project.H"
  45. #include "TLE.H"
  46. /* */
  47. #include "OSC_Thread.H"
  48. #include "OSC/Endpoint.H"
  49. #include <unistd.h>
  50. #include <nsm.h>
  51. extern nsm_client_t *nsm;
  52. #ifdef USE_WIDGET_FOR_TIMELINE
  53. #define BASE Fl_Group
  54. #define redraw_overlay() ((Fl_Overlay_Window*)window())->redraw_overlay()
  55. #define BX this->x()
  56. #define BY this->y()
  57. #else
  58. #ifdef USE_SINGLEBUFFERED_TIMELINE
  59. #warning Using singlebuffered timeline window. This may cause flicker and makes the cursors invisible.
  60. #define BASE Fl_Single_Window
  61. #define redraw_overlay()
  62. #else
  63. #define BASE Fl_Overlay_Window
  64. #endif
  65. #define BX 0
  66. #define BY 0
  67. #endif
  68. bool Timeline::draw_with_measure_lines = true;
  69. bool Timeline::draw_with_cursor_overlay = true;
  70. Timeline::snap_e Timeline::snap_to = Bars;
  71. bool Timeline::snapping_on_hold = false;
  72. bool Timeline::snap_magnetic = true;
  73. bool Timeline::follow_playhead = true;
  74. bool Timeline::center_playhead = true;
  75. bool Timeline::loop_playback = false;
  76. bool Timeline::automatically_create_takes = false;
  77. const float UPDATE_FREQ = 1.0 / 18.0f;
  78. extern const char *instance_name;
  79. extern TLE *tle;
  80. /** return the combined height of all visible children of (veritcal)
  81. pack, /p/. This is necessary because pack sizes are adjusted only
  82. when the relevant areas are exposes. */
  83. static int
  84. pack_visible_height ( const Fl_Pack *p )
  85. {
  86. int th = 0;
  87. const Fl_Widget* const *w = p->array();
  88. for ( int i = p->children(); i--; ++w )
  89. if ( (*w)->visible() )
  90. th += (*w)->h() + p->spacing();
  91. return th;
  92. }
  93. #define BP fl_begin_polygon()
  94. #define EP fl_end_polygon()
  95. #define vv(x,y) fl_vertex( x, y )
  96. #define BL fl_begin_line()
  97. #define EL fl_end_line()
  98. void
  99. draw_full_arrow_symbol ( Fl_Color color )
  100. {
  101. /* draw cap */
  102. fl_color( color );
  103. BP;
  104. vv( -1, -1 );
  105. vv( 0, 1 );
  106. vv( 1, -1 );
  107. EP;
  108. /* draw cap outline */
  109. fl_color( FL_BLACK );
  110. BL;
  111. vv( -1, -1 );
  112. vv( 0, 1 );
  113. vv( 1, -1 );
  114. EL;
  115. }
  116. nframes_t
  117. Timeline::range_start ( void ) const
  118. {
  119. if ( edit_cursor_track->active_cursor() )
  120. return edit_cursor_track->active_cursor()->start();
  121. else
  122. return 0;
  123. }
  124. nframes_t
  125. Timeline::range_end ( void ) const
  126. {
  127. if ( edit_cursor_track->active_cursor() )
  128. return edit_cursor_track->active_cursor()->start() + edit_cursor_track->active_cursor()->length();
  129. else
  130. return 0;
  131. }
  132. void
  133. Timeline::range_start ( nframes_t n )
  134. {
  135. if ( ! edit_cursor_track->active_cursor() )
  136. new Cursor_Region( 0, 0, "Edit", NULL );
  137. Logger log( edit_cursor_track->active_cursor() );
  138. edit_cursor_track->active_cursor()->set_left( n );
  139. }
  140. void
  141. Timeline::range_end ( nframes_t n )
  142. {
  143. if ( ! edit_cursor_track->active_cursor() )
  144. new Cursor_Region( 0, 0, "Edit", NULL );
  145. Logger log( edit_cursor_track->active_cursor() );
  146. edit_cursor_track->active_cursor()->set_right( n );
  147. }
  148. /** return first frame of playback (might not be 0) */
  149. nframes_t
  150. Timeline::playback_home ( void ) const
  151. {
  152. if ( play_cursor_track->active_cursor() )
  153. return play_cursor_track->active_cursor()->start();
  154. else
  155. return 0;
  156. }
  157. /** return last frame of playback */
  158. nframes_t
  159. Timeline::playback_end ( void ) const
  160. {
  161. if ( play_cursor_track->active_cursor() )
  162. return play_cursor_track->active_cursor()->start() + play_cursor_track->active_cursor()->length();
  163. else
  164. return length();
  165. }
  166. void
  167. Timeline::reset_range ( void )
  168. {
  169. delete edit_cursor_track->active_cursor();
  170. }
  171. /** callback used by Loggable class to create a snapshot of system
  172. * state. */
  173. void
  174. Timeline::snapshot ( void )
  175. {
  176. tempo_track->log_children();
  177. time_track->log_children();
  178. edit_cursor_track->log_children();
  179. punch_cursor_track->log_children();
  180. play_cursor_track->log_children();
  181. for ( int i = 0; i < tracks->children(); ++i )
  182. {
  183. ((Track*)tracks->child( i ))->log_children();
  184. }
  185. }
  186. /** recalculate the size of horizontal scrolling area and inform scrollbar */
  187. void
  188. Timeline::adjust_panzoomer ( void )
  189. {
  190. panzoomer->y_value( panzoomer->y_value(), h() - rulers->h() - panzoomer->h(), 0, pack_visible_height( tracks ));
  191. panzoomer->x_value( ts_to_x( xoffset ), tracks->w() - Track::width(), 0, ts_to_x( length() ) );
  192. }
  193. void
  194. Timeline::cb_scroll ( Fl_Widget *w, void *v )
  195. {
  196. ((Timeline*)v)->cb_scroll( w );
  197. }
  198. void
  199. Timeline::cb_scroll ( Fl_Widget *w )
  200. {
  201. //adjust_panzoomer();
  202. if ( panzoomer->zoom_changed() )
  203. {
  204. nframes_t under_mouse = x_to_offset( Fl::event_x() );
  205. _fpp = panzoomer->zoom();
  206. const int tw = tracks->w() - Track::width();
  207. panzoomer->x_value( ts_to_x( under_mouse ) );
  208. redraw();
  209. }
  210. if ( _old_yposition != panzoomer->y_value() )
  211. {
  212. tracks->position( tracks->x(), (rulers->y() + rulers->h()) - panzoomer->y_value() );
  213. damage( FL_DAMAGE_SCROLL );
  214. }
  215. if ( _old_xposition != x_to_ts( panzoomer->x_value() ))
  216. {
  217. damage( FL_DAMAGE_SCROLL );
  218. xposition( panzoomer->x_value() );
  219. }
  220. }
  221. void
  222. Timeline::menu_cb ( Fl_Widget *w, void *v )
  223. {
  224. ((Timeline*)v)->menu_cb( (Fl_Menu_*)w );
  225. }
  226. /** ensure that p1 is less than range_end() */
  227. void
  228. Timeline::fix_range ( void )
  229. {
  230. if ( range_start() > range_end() )
  231. {
  232. nframes_t t = range_end();
  233. range_end( range_start() );
  234. range_start( t );
  235. }
  236. }
  237. /** set the range to /start/ + /length/ */
  238. void
  239. Timeline::range ( nframes_t start, nframes_t length )
  240. {
  241. range_start( start );
  242. range_end( start + length );
  243. redraw();
  244. }
  245. /** create a new take for every armed track */
  246. void
  247. Timeline::add_take_for_armed_tracks ( void )
  248. {
  249. for ( int i = tracks->children(); i-- ; )
  250. {
  251. Track *t = (Track*)tracks->child( i );
  252. if ( t->armed() && t->sequence()->_widgets.size() )
  253. t->sequence( new Audio_Sequence( t ) );
  254. }
  255. }
  256. void
  257. Timeline::menu_cb ( Fl_Menu_ *m )
  258. {
  259. if ( ! active_r() )
  260. return;
  261. const char *picked = m->mvalue()->label();
  262. /* m->item_pathname( picked, sizeof( picked ) ); */
  263. DMESSAGE( "%s", picked );
  264. if ( ! strcmp( picked, "Add audio track" ) )
  265. {
  266. /* FIXME: prompt for I/O config? */
  267. Loggable::block_start();
  268. /* add audio track */
  269. char *name = get_unique_track_name( "Audio" );
  270. Track *t = new Track( name );
  271. Audio_Sequence *o = new Audio_Sequence( t );
  272. add_track( t );
  273. t->sequence( o );
  274. t->take_focus();
  275. Loggable::block_end();
  276. }
  277. else if ( ! strcmp( picked, "Tempo from edit (beat)" ) )
  278. {
  279. if ( range_start() != range_end() )
  280. {
  281. fix_range();
  282. beats_per_minute( range_start(), sample_rate() * 60 / (float)( range_end() - range_start() ) );
  283. range_end( range_start() );
  284. }
  285. }
  286. else if ( ! strcmp( picked, "Tempo from edit (bar)" ) )
  287. {
  288. if ( range_start() != range_end() )
  289. {
  290. fix_range();
  291. position_info pi = solve_tempomap( range_start() );
  292. beats_per_minute( range_start(), sample_rate() * 60 / (float)( ( range_end() - range_start() ) / pi.beats_per_bar ) );
  293. range_end( range_start() );
  294. }
  295. }
  296. else if ( ! strcmp( picked, "Playhead to mouse" ) )
  297. {
  298. int X = Fl::event_x() - Track::width();
  299. if ( X > 0 )
  300. {
  301. transport->locate( xoffset + x_to_ts( X ) );
  302. }
  303. }
  304. else if ( ! strcmp( picked, "Edit start to mouse" ) )
  305. {
  306. int X = Fl::event_x() - Track::width();
  307. if ( X > 0 )
  308. {
  309. range_start( xoffset + x_to_ts( X ) );
  310. }
  311. fix_range();
  312. /* FIXME: only needs to damage the location of the old cursor! */
  313. redraw();
  314. }
  315. else if ( ! strcmp( picked, "Edit end to mouse" ) )
  316. {
  317. int X = Fl::event_x() - Track::width();
  318. if ( X > 0 )
  319. {
  320. range_end( xoffset + x_to_ts( X ) );
  321. }
  322. fix_range();
  323. /* FIXME: only needs to damage the location of the old cursor! */
  324. redraw();
  325. }
  326. else if ( ! strcmp( picked, "Playhead left beat" ) )
  327. {
  328. nframes_t f = transport->frame;
  329. if ( prev_line( &f ) )
  330. transport->locate( f );
  331. }
  332. else if ( ! strcmp( picked, "Playhead right beat" ) )
  333. {
  334. nframes_t f = transport->frame;
  335. if ( next_line( &f ) )
  336. transport->locate( f );
  337. }
  338. else if ( ! strcmp( picked, "Playhead left bar" ) )
  339. {
  340. nframes_t f = transport->frame;
  341. if ( prev_line( &f, true ) )
  342. transport->locate( f );
  343. }
  344. else if ( ! strcmp( picked, "Playhead right bar" ) )
  345. {
  346. nframes_t f = transport->frame;
  347. if ( next_line( &f, true ) )
  348. transport->locate( f );
  349. }
  350. else if ( ! strcmp( picked, "Swap edit start and playhead" ) )
  351. {
  352. nframes_t t = transport->frame;
  353. transport->locate( range_start() );
  354. range_start( t );
  355. redraw();
  356. }
  357. else if ( ! strcmp( picked, "Swap edit end and playhead" ) )
  358. {
  359. nframes_t t = transport->frame;
  360. transport->locate( range_end() );
  361. range_end( t );
  362. redraw();
  363. }
  364. else if ( ! strcmp( picked, "Edit start to playhead" ) )
  365. {
  366. range_start( transport->frame );
  367. redraw();
  368. }
  369. else if ( ! strcmp( picked, "Edit end to playhead" ) )
  370. {
  371. range_end( transport->frame );
  372. redraw();
  373. }
  374. else if ( ! strcmp( picked, "Punch from edit" ) )
  375. {
  376. if ( range_start() != range_end() )
  377. {
  378. Loggable::block_start();
  379. Cursor_Region *o = new Cursor_Region( range_start(), range_end() - range_start(), "Punch", NULL );
  380. reset_range();
  381. Loggable::block_end();
  382. }
  383. redraw();
  384. }
  385. else if ( ! strcmp( picked, "Playback from edit" ) )
  386. {
  387. if ( range_start() != range_end() )
  388. {
  389. Loggable::block_start();
  390. if ( play_cursor_track->active_cursor() )
  391. {
  392. play_cursor_track->active_cursor()->start( range_start() );
  393. play_cursor_track->active_cursor()->set_right( range_end() );
  394. }
  395. else
  396. {
  397. Cursor_Region *o = new Cursor_Region( range_start(), range_end() - range_start(), "Playback", NULL );
  398. }
  399. reset_range();
  400. Loggable::block_end();
  401. }
  402. redraw();
  403. }
  404. else if ( ! strcmp( picked, "Redraw" ) )
  405. {
  406. redraw();
  407. }
  408. else
  409. WARNING( "programming error: Unknown menu item" );
  410. }
  411. int
  412. Timeline::ntracks ( void ) const
  413. {
  414. return tracks->children();
  415. }
  416. Timeline::~Timeline ( )
  417. {
  418. delete osc_thread;
  419. osc_thread = 0;
  420. }
  421. Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W, H, L )
  422. {
  423. Loggable::snapshot_callback( &Timeline::snapshot, this );
  424. edit_cursor_track = NULL;
  425. punch_cursor_track = NULL;
  426. play_cursor_track = NULL;
  427. _created_new_takes = 0;
  428. _punched_in = 0;
  429. _punch_in_frame = 0;
  430. _punch_out_frame = 0;
  431. osc_thread = 0;
  432. _sample_rate = 44100;
  433. box( FL_FLAT_BOX );
  434. xoffset = 0;
  435. _old_yposition = 0;
  436. _old_xposition = 0;
  437. #ifndef USE_WIDGET_FOR_TIMELINE
  438. X = Y = 0;
  439. #endif
  440. // range_start( range_end( 0 ) );
  441. menu = new Fl_Menu_Button( 0, 0, 0, 0, "Timeline" );
  442. /* menu->add( "Add Track", 0, 0, 0 ); */
  443. menu->add( "Add audio track", 'a', 0, 0 );
  444. menu->add( "Tempo from edit (beat)", 't', 0, 0 );
  445. menu->add( "Tempo from edit (bar)", FL_CTRL + 't', 0, 0 );
  446. menu->add( "Playhead to mouse", 'p', 0, 0 );
  447. menu->add( "Edit start to mouse", '[', 0, 0 );
  448. menu->add( "Edit end to mouse", ']', 0, 0 );
  449. menu->add( "Playhead left beat", FL_SHIFT + FL_Left, 0, 0 );
  450. menu->add( "Playhead right beat", FL_SHIFT + FL_Right, 0, 0 );
  451. menu->add( "Playhead left bar", FL_CTRL + FL_SHIFT + FL_Left, 0, 0 );
  452. menu->add( "Playhead right bar", FL_CTRL + FL_SHIFT + FL_Right, 0, 0 );
  453. menu->add( "Swap edit start and playhead", FL_CTRL + FL_SHIFT + '[', 0, 0 );
  454. menu->add( "Swap edit end and playhead", FL_CTRL + FL_SHIFT + ']', 0, 0 );
  455. menu->add( "Edit start to playhead", FL_CTRL + '[', 0, 0 );
  456. menu->add( "Edit end to playhead", FL_CTRL + ']', 0, 0 );
  457. menu->add( "Punch from edit", FL_CTRL + FL_SHIFT + 'p', 0, 0 );
  458. menu->add( "Playback from edit", FL_CTRL + FL_SHIFT + 'l', 0, 0 );
  459. menu->add( "Redraw", FL_CTRL + 'l', 0, 0 );
  460. menu_set_callback( const_cast<Fl_Menu_Item*>(menu->menu()), &Timeline::menu_cb, (void*)this );
  461. {
  462. Fl_Panzoomer *o = new Fl_Panzoomer( X, Y + H - 100, W, 100 );
  463. // o->range( 0, 48000 * 300 );
  464. // o->zoom_range( 1, 16384 );
  465. // o->zoom_range( 1, 65536 << 4 );
  466. o->zoom_range( 1, 20 );
  467. o->zoom( 8 );
  468. o->box( FL_FLAT_BOX );
  469. o->color( FL_BACKGROUND_COLOR );
  470. o->type( FL_HORIZONTAL );
  471. o->callback( cb_scroll, this );
  472. o->draw_thumbnail_view_callback( &Timeline::draw_thumbnail_view, this );
  473. panzoomer = o;
  474. }
  475. {
  476. Fl_Pack *o = new Fl_Pack( X + Track::width(), Y, (W - Track::width()), H - panzoomer->h(), "rulers" );
  477. o->type( Fl_Pack::VERTICAL );
  478. {
  479. Tempo_Sequence *o = new Tempo_Sequence( 0, 0, 800, 18 );
  480. o->color( FL_GRAY );
  481. o->labelsize( 12 );
  482. o->label( "Tempo" );
  483. o->align( FL_ALIGN_LEFT );
  484. tempo_track = o;
  485. }
  486. {
  487. Time_Sequence *o = new Time_Sequence( 0, 24, 800, 18 );
  488. o->color( fl_lighter( FL_GRAY ) );
  489. o->labelsize( 12 );
  490. o->label( "Time" );
  491. o->align( FL_ALIGN_LEFT );
  492. time_track = o;
  493. }
  494. {
  495. Cursor_Sequence *o = new Cursor_Sequence( 0, 24, 800, 18 );
  496. o->color( FL_GRAY );
  497. o->labelsize( 12 );
  498. o->label( "Edit" );
  499. o->align( FL_ALIGN_LEFT );
  500. o->cursor_color( FL_YELLOW );
  501. edit_cursor_track = o;
  502. }
  503. {
  504. Cursor_Sequence *o = new Cursor_Sequence( 0, 24, 800, 18 );
  505. o->color( fl_lighter( FL_GRAY ) );
  506. o->labelsize( 12 );
  507. o->label( "Punch" );
  508. o->align( FL_ALIGN_LEFT );
  509. o->cursor_color( FL_RED );
  510. punch_cursor_track = o;
  511. }
  512. {
  513. Cursor_Sequence *o = new Cursor_Sequence( 0, 24, 800, 18 );
  514. o->color( FL_GRAY );
  515. o->labelsize( 12 );
  516. o->label( "Playback" );
  517. o->align( FL_ALIGN_LEFT );
  518. o->cursor_color( FL_GREEN );
  519. play_cursor_track = o;
  520. }
  521. /* { */
  522. /* Annotation_Sequence *o = new Annotation_Sequence( 0, 24, 800, 24 ); */
  523. /* o->color( fl_gray_ramp( 'F' ) ); */
  524. /* o->label( "Ruler" ); */
  525. /* o->align( FL_ALIGN_LEFT ); */
  526. /* ruler_track = o; */
  527. /* } */
  528. rulers = o;
  529. o->size( o->w(), o->child( 0 )->h() * o->children() );
  530. o->end();
  531. }
  532. {
  533. // sample_rate() = engine->sample_rate();
  534. _fpp = 8;
  535. // length() = sample_rate() * 60 * 2;
  536. /* FIXME: hack */
  537. // length() = x_to_ts( W );
  538. {
  539. Fl_Pack *o = new Fl_Pack( X, rulers->y() + rulers->h(), W, 1 );
  540. o->type( Fl_Pack::VERTICAL );
  541. o->spacing( 1 );
  542. tracks = o;
  543. o->end();
  544. resizable( o );
  545. }
  546. }
  547. /* rulers go above tracks... */
  548. add( rulers );
  549. /* make sure scrollbars are on top */
  550. add( panzoomer );
  551. // vscroll->range( 0, tracks->h() );
  552. redraw();
  553. end();
  554. Fl::add_timeout( UPDATE_FREQ, update_cb, this );
  555. }
  556. void
  557. Timeline::beats_per_minute ( nframes_t when, float bpm )
  558. {
  559. tempo_track->add( new Tempo_Point( when, bpm ) );
  560. }
  561. void
  562. Timeline::time ( nframes_t when, int bpb, int note_type )
  563. {
  564. time_track->add( new Time_Point( when, bpb, note_type ) );
  565. }
  566. /************/
  567. /* Snapping */
  568. /************/
  569. struct nearest_line_arg
  570. {
  571. nframes_t original;
  572. nframes_t closest;
  573. bool bar;
  574. };
  575. const int snap_pixel = 10;
  576. static nframes_t
  577. abs_diff ( nframes_t n1, nframes_t n2 )
  578. {
  579. return n1 > n2 ? n1 - n2 : n2 - n1;
  580. }
  581. static void
  582. nearest_line_snap_cb ( nframes_t frame, const BBT &bbt, void *arg )
  583. {
  584. nearest_line_arg *n = (nearest_line_arg *)arg;
  585. if ( n->bar && bbt.beat )
  586. return;
  587. if ( Timeline::snap_magnetic &&
  588. abs_diff( frame, n->original ) > timeline->x_to_ts( snap_pixel ) )
  589. return;
  590. if ( abs_diff( frame, n->original ) < abs_diff( n->original, n->closest ) )
  591. n->closest = frame;
  592. }
  593. static void
  594. nearest_line_cb ( nframes_t frame, const BBT &bbt, void *arg )
  595. {
  596. nearest_line_arg *n = (nearest_line_arg *)arg;
  597. if ( n->bar && bbt.beat )
  598. return;
  599. if ( abs_diff( frame, n->original ) < abs_diff( n->original, n->closest ) )
  600. n->closest = frame;
  601. }
  602. static void
  603. prev_next_line_cb ( nframes_t frame, const BBT &bbt, void *arg )
  604. {
  605. nearest_line_arg *n = (nearest_line_arg *)arg;
  606. if ( n->bar && bbt.beat )
  607. return;
  608. if ( abs_diff( frame, n->original ) < abs_diff( n->original, n->closest ) )
  609. n->closest = frame;
  610. }
  611. /** Set the value pointed to by /frame/ to the frame number of the of
  612. the nearest measure line to /when/. Returns true if the new value of
  613. *frame is valid, false otherwise. */
  614. bool
  615. Timeline::nearest_line ( nframes_t *frame, bool snap ) const
  616. {
  617. if ( snap && ( snapping_on_hold || None == Timeline::snap_to ) )
  618. return false;
  619. nframes_t when = *frame;
  620. nearest_line_arg n = { when, -1, snap && Timeline::Bars == Timeline::snap_to };
  621. render_tempomap( when > x_to_ts( w() >> 1 ) ? when - x_to_ts( w() >> 1 ) : 0,
  622. when + x_to_ts( w() >> 1 ), snap ? nearest_line_snap_cb : nearest_line_cb, &n );
  623. if ( n.closest == (nframes_t)-1 )
  624. return false;
  625. else
  626. {
  627. *frame = n.closest;
  628. return true;
  629. }
  630. }
  631. /** Set the value pointed to by /frame/ to the frame number of the of
  632. the nearest measure line to *greater than* /when/. Returns true if
  633. the new value of *frame is valid, false otherwise. */
  634. bool
  635. Timeline::next_line ( nframes_t *frame, bool bar ) const
  636. {
  637. nframes_t when = *frame + 1;
  638. nearest_line_arg n = { when, -1, bar };
  639. render_tempomap( when, x_to_ts( w() ), prev_next_line_cb, &n );
  640. if ( n.closest == (nframes_t)-1 )
  641. return false;
  642. else
  643. {
  644. *frame = n.closest;
  645. return true;
  646. }
  647. }
  648. /** Set the value pointed to by /frame/ to the frame number of the of
  649. the nearest measure line to *less than* /when/. Returns true if
  650. the new value of *frame is valid, false otherwise. */
  651. bool
  652. Timeline::prev_line ( nframes_t *frame, bool bar ) const
  653. {
  654. nframes_t when = *frame - 1;
  655. nearest_line_arg n = { when, -1, bar };
  656. render_tempomap( xoffset, when - xoffset, prev_next_line_cb, &n );
  657. if ( n.closest == (nframes_t)-1 )
  658. return false;
  659. else
  660. {
  661. *frame = n.closest;
  662. return true;
  663. }
  664. }
  665. /** given screen pixel coordinate /x/ return frame offset into
  666. * timeline, taking into account the current scroll position, widget
  667. * layout, etc. */
  668. nframes_t
  669. Timeline::x_to_offset ( int x ) const
  670. {
  671. return x_to_ts( max( 0, x - Track::width() ) ) + xoffset;
  672. }
  673. /** draws a single measure line */
  674. static void
  675. draw_measure_cb ( nframes_t frame, const BBT &bbt, void * )
  676. {
  677. Fl_Color c = FL_LIGHT3;
  678. if ( bbt.beat )
  679. c = FL_DARK1;
  680. fl_color( fl_color_add_alpha( c, 64 ) );
  681. const int x = timeline->ts_to_x( frame - timeline->xoffset ) + Track::width();
  682. fl_line( x, 0, x, 2000 );
  683. }
  684. /* FIXME: wrong place for this */
  685. const float ticks_per_beat = 1920.0;
  686. /** re-render the unified tempomap based on the current contents of the Time and Tempo sequences */
  687. void
  688. Timeline::update_tempomap ( void )
  689. {
  690. /* FIXME: we need some type of locking! */
  691. _tempomap.clear();
  692. for ( list <Sequence_Widget *>::const_iterator i = time_track->_widgets.begin();
  693. i != time_track->_widgets.end(); ++i )
  694. _tempomap.push_back( *i );
  695. for ( list <Sequence_Widget *>::const_iterator i = tempo_track->_widgets.begin();
  696. i != tempo_track->_widgets.end(); ++i )
  697. _tempomap.push_back( *i );
  698. _tempomap.sort( Sequence_Widget::sort_func );
  699. }
  700. /** return a stucture containing the BBT info which applies at /frame/ */
  701. position_info
  702. Timeline::solve_tempomap ( nframes_t frame ) const
  703. {
  704. return render_tempomap( frame, 0, 0, 0 );
  705. }
  706. /* THREAD: UI and RT */
  707. /** draw appropriate measure lines inside the given bounding box */
  708. position_info
  709. Timeline::render_tempomap( nframes_t start, nframes_t length, measure_line_callback * cb, void *arg ) const
  710. {
  711. const nframes_t end = start + length;
  712. position_info pos;
  713. memset( &pos, 0, sizeof( pos ) );
  714. BBT &bbt = pos.bbt;
  715. /* default values */
  716. pos.beat_type = 4;
  717. pos.beats_per_bar = 4;
  718. pos.tempo = 120.0;
  719. const nframes_t samples_per_minute = sample_rate() * 60;
  720. float bpm = 120.0f;
  721. time_sig sig;
  722. sig.beats_per_bar = 4;
  723. sig.beat_type = 4;
  724. nframes_t f = 0;
  725. nframes_t next = 0;
  726. nframes_t frames_per_beat = samples_per_minute / bpm;
  727. if ( ! _tempomap.size() )
  728. return pos;
  729. for ( list <const Sequence_Widget *>::const_iterator i = _tempomap.begin();
  730. i != _tempomap.end(); ++i )
  731. {
  732. if ( ! strcmp( (*i)->class_name(), "Tempo_Point" ) )
  733. {
  734. const Tempo_Point *p = (Tempo_Point*)(*i);
  735. bpm = p->tempo();
  736. frames_per_beat = samples_per_minute / bpm;
  737. }
  738. else
  739. {
  740. const Time_Point *p = (Time_Point*)(*i);
  741. sig = p->time();
  742. /* Time point resets beat */
  743. bbt.beat = 0;
  744. }
  745. {
  746. list <const Sequence_Widget *>::const_iterator n = i;
  747. ++n;
  748. if ( n == _tempomap.end() )
  749. next = end;
  750. else
  751. // next = min( (*n)->start(), end );
  752. /* points may not always be aligned with beat boundaries, so we must align here */
  753. next = (*n)->start() - ( ( (*n)->start() - (*i)->start() ) % frames_per_beat );
  754. }
  755. for ( ; f < next; ++bbt.beat, f += frames_per_beat )
  756. {
  757. if ( bbt.beat == sig.beats_per_bar )
  758. {
  759. bbt.beat = 0;
  760. ++bbt.bar;
  761. }
  762. if ( f >= start )
  763. {
  764. /* in the zone */
  765. if ( cb )
  766. cb( f, bbt, arg );
  767. }
  768. /* ugliness to avoid failing out at -1 */
  769. if ( end >= frames_per_beat )
  770. {
  771. if ( f >= end - frames_per_beat )
  772. goto done;
  773. }
  774. else if ( f + frames_per_beat >= end )
  775. goto done;
  776. }
  777. }
  778. done:
  779. pos.frame = f;
  780. pos.tempo = bpm;
  781. pos.beats_per_bar = sig.beats_per_bar;
  782. pos.beat_type = sig.beat_type;
  783. assert( f <= end );
  784. assert( end - f <= frames_per_beat );
  785. /* FIXME: this this right? */
  786. const double frames_per_tick = frames_per_beat / ticks_per_beat;
  787. bbt.tick = ( end - f ) / frames_per_tick;
  788. return pos;
  789. }
  790. /** maybe draw appropriate measure lines in rectangle defined by X, Y, W, and H, using color /color/ as a base */
  791. void
  792. Timeline::draw_measure_lines ( int X, int Y, int W, int H )
  793. {
  794. if ( ! draw_with_measure_lines )
  795. return;
  796. fl_line_style( FL_SOLID, 0 );
  797. const nframes_t start = x_to_offset( X );
  798. const nframes_t length = x_to_ts( W );
  799. fl_push_clip( X, Y, W, H );
  800. render_tempomap( start, length, draw_measure_cb, NULL );
  801. fl_pop_clip();
  802. }
  803. void
  804. Timeline::draw_clip ( void * v, int X, int Y, int W, int H )
  805. {
  806. Timeline *tl = (Timeline *)v;
  807. fl_push_clip( X, Y, W, H );
  808. /* fl_color( rand() ); */
  809. /* fl_rectf( X, Y, X + W, Y + H ); */
  810. tl->draw_box();
  811. tl->draw_child( *tl->rulers );
  812. fl_push_clip( tl->tracks->x(), tl->rulers->y() + tl->rulers->h(), tl->tracks->w(), tl->h() - tl->rulers->h() - tl->panzoomer->h() );
  813. tl->draw_child( *tl->tracks );
  814. tl->draw_cursors();
  815. fl_pop_clip();
  816. fl_pop_clip();
  817. }
  818. /** handle resize event */
  819. void
  820. Timeline::resize ( int X, int Y, int W, int H )
  821. {
  822. BASE::resize( X, Y, W, H );
  823. /* why is this necessary? */
  824. rulers->resize( BX + Track::width(), BY, W - Track::width(), rulers->h() );
  825. /* why is THIS necessary? */
  826. panzoomer->resize( BX, BY + H - 100, panzoomer->w(), 100 );
  827. tracks->resize( BX, BY + rulers->h(), W, H - panzoomer->h() );
  828. }
  829. void
  830. Timeline::add_cursor ( Cursor_Region *o )
  831. {
  832. if ( !strcmp( o->type(), "Edit" ) )
  833. {
  834. DMESSAGE( "Adding cursor to edit track" );
  835. edit_cursor_track->add( o );
  836. }
  837. else if ( !strcmp( o->type(), "Punch" ) )
  838. {
  839. DMESSAGE( "Adding cursor to punch track" );
  840. punch_cursor_track->add( o );
  841. }
  842. else if ( !strcmp( o->type(), "Playback" ) )
  843. {
  844. DMESSAGE( "Adding cursor to punch track" );
  845. play_cursor_track->add( o );
  846. }
  847. }
  848. void
  849. Timeline::add_cursor ( Cursor_Point *o )
  850. {
  851. if ( !strcmp( o->type(), "Edit" ) )
  852. edit_cursor_track->add( o );
  853. else if ( !strcmp( o->type(), "Punch" ) )
  854. punch_cursor_track->add( o );
  855. }
  856. void
  857. Timeline::draw_thumbnail_view ( int X, int Y, int W, int H, void *v )
  858. {
  859. ((Timeline*)v)->draw_thumbnail_view( X,Y,W,H );
  860. }
  861. void
  862. Timeline::draw_thumbnail_view ( int X, int Y, int W, int H ) const
  863. {
  864. nframes_t sf = 0;
  865. nframes_t ef = timeline->length();
  866. double ty = Y;
  867. for ( int i = 0; i < timeline->tracks->children(); i++ )
  868. {
  869. Track *t = (Track*)timeline->tracks->child( i );
  870. Sequence *s = t->sequence();
  871. if ( !s )
  872. continue;
  873. fl_color( FL_BLACK );
  874. const double scale = (double)H / ( pack_visible_height( tracks ) );
  875. // double th = (double)H / timeline->tracks->children();
  876. const double th = t->h() * scale;
  877. fl_line( X, ty,
  878. X + W, ty );
  879. for ( list <Sequence_Widget *>::const_iterator r = s->_widgets.begin();
  880. r != s->_widgets.end(); ++r )
  881. {
  882. fl_rectf(
  883. X + ( W * ( (double)(*r)->start() / ef ) ),
  884. ty,
  885. W * ( (double)(*r)->length() / ef ),
  886. th,
  887. (*r)->actual_box_color());
  888. }
  889. fl_font( FL_HELVETICA, th );
  890. fl_color( FL_FOREGROUND_COLOR );
  891. fl_draw( t->name(), X, ty, W, th, (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_INSIDE ));
  892. ty += th;
  893. }
  894. }
  895. void
  896. Timeline::draw_cursors ( Cursor_Sequence *o ) const
  897. {
  898. fl_push_clip( tracks->x() + Track::width(), rulers->y() + rulers->h(), tracks->w() - Track::width(), h() - rulers->h() - panzoomer->h() );
  899. if ( o && o->_widgets.size() > 0 )
  900. {
  901. for ( std::list<Sequence_Widget*>::const_iterator i = o->_widgets.begin();
  902. i != o->_widgets.end();
  903. i++ )
  904. {
  905. if ( Timeline::draw_with_cursor_overlay )
  906. {
  907. fl_color( fl_color_add_alpha( (*i)->box_color(), 50 ) );
  908. fl_rectf( (*i)->line_x(), tracks->y(), (*i)->abs_w(), tracks->h() );
  909. }
  910. fl_color( fl_color_add_alpha( (*i)->box_color(), 127 ));
  911. fl_line( (*i)->line_x(), tracks->y(), (*i)->line_x(), 9000 );
  912. fl_line( (*i)->line_x() + (*i)->abs_w(), tracks->y(), (*i)->line_x() + (*i)->abs_w(), tracks->h() );
  913. }
  914. }
  915. fl_pop_clip();
  916. }
  917. /** draw ancillary cursors (not necessarily in the overlay plane) */
  918. void
  919. Timeline::draw_cursors ( void ) const
  920. {
  921. draw_cursors( edit_cursor_track );
  922. if ( transport->punch_enabled() )
  923. draw_cursors( punch_cursor_track );
  924. }
  925. void
  926. Timeline::draw ( void )
  927. {
  928. int X, Y, W, H;
  929. int bdx = 0;
  930. int bdw = 0;
  931. X = tracks->x() + bdx + 1;
  932. Y = tracks->y();
  933. W = tracks->w() - bdw - 1;
  934. H = tracks->h();
  935. adjust_panzoomer();
  936. panzoomer->redraw();
  937. int dx = ts_to_x( _old_xposition ) - ts_to_x( xoffset );
  938. int dy = _old_yposition - panzoomer->y_value();
  939. int c = damage();
  940. if ( c & FL_DAMAGE_SCROLL )
  941. {
  942. /* if ( dx && dy ) */
  943. /* { */
  944. /* /\* FIXME: scrolling both at once is... problematic *\/ */
  945. /* c |= FL_DAMAGE_ALL; */
  946. /* } */
  947. /* else */
  948. {
  949. /* draw_child( *rulers ); */
  950. Y = rulers->y() + rulers->h();
  951. H = h() - rulers->h() - panzoomer->h();
  952. if ( dy )
  953. fl_scroll( X, Y, Track::width(), H, 0, dy, draw_clip, this );
  954. if ( dx )
  955. fl_scroll( rulers->x(), rulers->y(), rulers->w(), rulers->h(), dx, 0, draw_clip, this );
  956. fl_scroll( X + Track::width(), Y, W - Track::width(), H, dx, dy, draw_clip, this );
  957. }
  958. }
  959. if ( c & FL_DAMAGE_ALL )
  960. {
  961. DMESSAGE( "complete redraw" );
  962. draw_box( box(), BX, BY, w(), h(), color() );
  963. fl_push_clip( BX, rulers->y(), w(), rulers->h() );
  964. draw_child( *rulers );
  965. fl_pop_clip();
  966. fl_push_clip( tracks->x(), rulers->y() + rulers->h(), tracks->w(), panzoomer->y() - (rulers->y() + rulers->h()) );
  967. draw_child( *tracks );
  968. draw_cursors();
  969. fl_pop_clip();
  970. draw_child( *panzoomer );
  971. redraw_overlay();
  972. goto done;
  973. }
  974. if ( c & FL_DAMAGE_CHILD )
  975. {
  976. fl_push_clip( rulers->x(), rulers->y(), rulers->w(), rulers->h() );
  977. update_child( *rulers );
  978. fl_pop_clip();
  979. /* if ( ! ( damage() & FL_DAMAGE_SCROLL ) ) */
  980. /* { */
  981. fl_push_clip( tracks->x(), rulers->y() + rulers->h(), tracks->w(), h() - rulers->h() - panzoomer->h() );
  982. update_child( *tracks );
  983. draw_cursors();
  984. fl_pop_clip();
  985. /* } */
  986. update_child( *panzoomer );
  987. }
  988. done:
  989. /* panzoomer->redraw(); */
  990. /* update_child( *panzoomer ); */
  991. _old_xposition = xoffset;
  992. _old_yposition = panzoomer->y_value();
  993. }
  994. /** draw a single cursor line at /frame/ with color /color/ using symbol routine /symbol/ for the cap */
  995. void
  996. Timeline::draw_cursor ( nframes_t frame, Fl_Color color, void (*symbol)(Fl_Color) ) const
  997. {
  998. // int x = ( ts_to_x( frame ) - ts_to_x( xoffset ) ) + tracks->x() + Track::width();
  999. if ( frame < xoffset )
  1000. return;
  1001. const int x = ts_to_x( frame - xoffset ) + tracks->x() + Track::width();
  1002. if ( x > tracks->x() + tracks->w() )
  1003. return;
  1004. const int y = rulers->y() + rulers->h();
  1005. const int h = this->h() - rulers->h() - panzoomer->h();
  1006. fl_push_clip( tracks->x() + Track::width(), y, tracks->w(), h );
  1007. fl_line_style( FL_SOLID, 0 );
  1008. fl_color( color );
  1009. fl_line( x, y, x, y + h );
  1010. fl_push_matrix();
  1011. fl_translate( x, y );
  1012. fl_scale( 8, 4 );
  1013. symbol( color );
  1014. fl_pop_matrix();
  1015. fl_pop_clip();
  1016. }
  1017. void
  1018. Timeline::draw_playhead ( void )
  1019. {
  1020. draw_cursor( transport->frame, FL_RED, draw_full_arrow_symbol );
  1021. // draw_cursor( length(), FL_BLACK, draw_full_arrow_symbol );
  1022. }
  1023. void
  1024. Timeline::redraw_playhead ( void )
  1025. {
  1026. static nframes_t last_playhead = -1;
  1027. static int last_playhead_x = -1;
  1028. /* FIXME: kind of a hackish way to invoke punch / looping stuff from the UI thread... */
  1029. if ( transport->rolling &&
  1030. transport->rec_enabled() &&
  1031. transport->punch_enabled() )
  1032. {
  1033. if ( _punched_in &&
  1034. transport->frame > _punch_in_frame &&
  1035. transport->frame > _punch_out_frame )
  1036. {
  1037. punch_out( _punch_out_frame );
  1038. }
  1039. else if ( ! _punched_in )
  1040. {
  1041. /* we've passed one or more punch regions... punch in for the next, if available. */
  1042. const Sequence_Widget *w = punch_cursor_track->next( transport->frame );
  1043. if ( w &&
  1044. w->start() > transport->frame )
  1045. {
  1046. _punch_in_frame = w->start();
  1047. _punch_out_frame = w->start() + w->length();
  1048. punch_in( w->start() );
  1049. }
  1050. }
  1051. }
  1052. if ( transport->rolling )
  1053. {
  1054. if ( play_cursor_track->active_cursor() )
  1055. {
  1056. if ( Timeline::loop_playback )
  1057. {
  1058. if ( transport->frame > playback_end() )
  1059. {
  1060. if ( ! seek_pending() )
  1061. {
  1062. if ( transport->recording )
  1063. {
  1064. stop();
  1065. transport->locate( playback_home() );
  1066. record();
  1067. }
  1068. else
  1069. {
  1070. transport->locate( playback_home() );
  1071. }
  1072. }
  1073. }
  1074. }
  1075. else
  1076. if ( transport->frame > playback_end() )
  1077. transport->stop();
  1078. }
  1079. }
  1080. int playhead_x = ts_to_x( transport->frame );
  1081. if ( last_playhead_x != playhead_x )
  1082. {
  1083. redraw_overlay();
  1084. last_playhead = transport->frame;
  1085. last_playhead_x = playhead_x;
  1086. if ( follow_playhead )
  1087. {
  1088. if ( center_playhead && active() )
  1089. xposition( max( 0, playhead_x - ( ( tracks->w() - Track::width() ) >> 1 ) ) );
  1090. else if ( playhead_x > ts_to_x( xoffset ) + ( tracks->w() - Track::width() ) )
  1091. xposition( playhead_x );
  1092. adjust_panzoomer();
  1093. }
  1094. }
  1095. }
  1096. /** called so many times a second to redraw the playhead etc. */
  1097. void
  1098. Timeline::update_cb ( void *arg )
  1099. {
  1100. Fl::repeat_timeout( UPDATE_FREQ, update_cb, arg );
  1101. Timeline *tl = (Timeline *)arg;
  1102. tl->redraw_playhead();
  1103. }
  1104. /** draw cursors in overlay plane */
  1105. void
  1106. Timeline::draw_overlay ( void )
  1107. {
  1108. fl_push_no_clip();
  1109. draw_playhead();
  1110. if ( ! ( _selection.w && _selection.h ) )
  1111. {
  1112. fl_pop_clip();
  1113. return;
  1114. }
  1115. fl_push_clip( tracks->x() + Track::width(), rulers->y() + rulers->h(), tracks->w() - Track::width(), h() - rulers->h() - panzoomer->h() );
  1116. const Rectangle &r = _selection;
  1117. fl_color( FL_MAGENTA );
  1118. fl_line_style( FL_SOLID, 0 );
  1119. fl_rect( r.x, r.y, r.w, r.h );
  1120. fl_pop_clip();
  1121. fl_pop_clip();
  1122. }
  1123. /** select sequence widgets within rectangle /r/ */
  1124. void
  1125. Timeline::select ( const Rectangle &r )
  1126. {
  1127. const int Y = r.y;
  1128. for ( int i = tracks->children(); i-- ; )
  1129. {
  1130. Track *t = (Track*)tracks->child( i );
  1131. if ( ! ( t->y() > Y + r.h || t->y() + t->h() < Y ) )
  1132. t->select( r.x, r.y, r.w, r.h, true, true );
  1133. }
  1134. }
  1135. /** delete all selected sequence widgets */
  1136. void
  1137. Timeline::delete_selected ( void )
  1138. {
  1139. Sequence_Widget::delete_selected();
  1140. }
  1141. /** clear the selection of seqeunce widgets */
  1142. void
  1143. Timeline::select_none ( void )
  1144. {
  1145. Sequence_Widget::select_none();
  1146. }
  1147. int
  1148. Timeline::nselected ( void ) const
  1149. {
  1150. return Sequence_Widget::nselected();
  1151. }
  1152. /** An unfortunate necessity for implementing our own DND aside from
  1153. * the (bogus) native FLTK system */
  1154. Track *
  1155. Timeline::track_under ( int Y )
  1156. {
  1157. for ( int i = tracks->children(); i-- ; )
  1158. {
  1159. Track *t = (Track*)tracks->child( i );
  1160. if ( ! ( t->y() > Y || t->y() + t->h() < Y ) )
  1161. return t;
  1162. }
  1163. return NULL;
  1164. }
  1165. #include "FL/event_name.H"
  1166. #include "FL/test_press.H"
  1167. /** a bit of a hack to keep FLTK's focus navigation stuff from
  1168. * stealing the arrow keys from us */
  1169. int
  1170. Timeline::handle_scroll ( int m )
  1171. {
  1172. if ( m == FL_KEYBOARD &&
  1173. Fl::event_key() != FL_Home &&
  1174. Fl::event_key() != FL_End )
  1175. return menu->test_shortcut() || panzoomer->handle( m );
  1176. else
  1177. return 0;
  1178. }
  1179. int
  1180. Timeline::handle ( int m )
  1181. {
  1182. static Drag *drag = NULL;
  1183. static bool range = false;
  1184. /* if ( m != FL_NO_EVENT ) */
  1185. /* DMESSAGE( "%s", event_name( m ) ); */
  1186. /* int r = BASE::handle( m ); */
  1187. switch ( m )
  1188. {
  1189. case FL_ENTER:
  1190. return 1;
  1191. case FL_LEAVE:
  1192. return 1;
  1193. case FL_KEYDOWN:
  1194. if ( Fl::event_state() & ( FL_ALT | FL_CTRL | FL_SHIFT ) )
  1195. /* we don't want any keys with modifiers... */
  1196. return 0;
  1197. if ( Fl::event_key() == 'r' )
  1198. {
  1199. range = true;
  1200. return 1;
  1201. }
  1202. else if ( Fl::event_key() == 's' )
  1203. {
  1204. snapping_on_hold = true;
  1205. return 1;
  1206. }
  1207. return 0;
  1208. case FL_KEYUP:
  1209. if ( Fl::event_state() & ( FL_ALT | FL_CTRL | FL_SHIFT ) )
  1210. /* we don't want any keys with modifiers... */
  1211. return 0;
  1212. if ( Fl::event_key() == 'r' )
  1213. {
  1214. range = false;
  1215. return 1;
  1216. }
  1217. else if ( Fl::event_key() == 's' )
  1218. {
  1219. snapping_on_hold = false;
  1220. return 1;
  1221. }
  1222. return 0;
  1223. // case FL_KEYBOARD:
  1224. case FL_SHORTCUT:
  1225. {
  1226. if ( Fl::event_state() & ( FL_ALT | FL_CTRL | FL_SHIFT ) )
  1227. /* we don't want any keys with modifiers... */
  1228. return 0;
  1229. switch ( Fl::event_key() )
  1230. {
  1231. case FL_Delete:
  1232. case FL_Home:
  1233. case FL_End:
  1234. /* keep scrollbar from eating these. */
  1235. return 0;
  1236. default:
  1237. return BASE::handle( m );
  1238. }
  1239. return 0;
  1240. }
  1241. default:
  1242. {
  1243. int r = BASE::handle( m );
  1244. if ( m != FL_RELEASE && r )
  1245. return r;
  1246. const int X = Fl::event_x();
  1247. const int Y = Fl::event_y();
  1248. switch ( m )
  1249. {
  1250. case FL_PUSH:
  1251. {
  1252. if ( test_press( FL_BUTTON1 ) || test_press( FL_BUTTON1 + FL_CTRL ) )
  1253. {
  1254. assert( ! drag );
  1255. drag = new Drag( X, Y );
  1256. _selection.x = X;
  1257. _selection.y = Y;
  1258. if ( ! Fl::event_ctrl() )
  1259. select_none();
  1260. return 1;
  1261. }
  1262. else if ( test_press( FL_BUTTON3 ) )
  1263. {
  1264. menu_popup( menu );
  1265. return 1;
  1266. }
  1267. return 0;
  1268. }
  1269. case FL_DRAG:
  1270. {
  1271. int ox = X - drag->x;
  1272. int oy = Y - drag->y;
  1273. if ( ox < 0 )
  1274. _selection.x = X;
  1275. if ( oy < 0 )
  1276. _selection.y = Y;
  1277. _selection.w = abs( ox );
  1278. _selection.h = abs( oy );
  1279. if ( range )
  1280. {
  1281. range_start( x_to_offset( _selection.x ) );
  1282. range_end( x_to_offset( _selection.x + _selection.w ) );
  1283. redraw();
  1284. }
  1285. redraw_overlay();
  1286. return 1;
  1287. break;
  1288. }
  1289. case FL_RELEASE:
  1290. {
  1291. delete drag;
  1292. drag = NULL;
  1293. if ( range )
  1294. {
  1295. range_start( x_to_offset( _selection.x ) );
  1296. range_end( x_to_offset( _selection.x + _selection.w ) );
  1297. redraw();
  1298. }
  1299. else
  1300. select( _selection );
  1301. _selection.x = _selection.y =_selection.w = _selection.h = 0;
  1302. redraw_overlay();
  1303. return 1;
  1304. }
  1305. default:
  1306. return 0;
  1307. break;
  1308. }
  1309. return 0;
  1310. }
  1311. }
  1312. }
  1313. /** retrun a pointer to the track named /name/, or NULL if no track is named /name/ */
  1314. Track *
  1315. Timeline::track_by_name ( const char *name )
  1316. {
  1317. for ( int i = tracks->children(); i-- ; )
  1318. {
  1319. Track *t = (Track*)tracks->child( i );
  1320. if ( ! strcmp( name, t->name() ) )
  1321. return t;
  1322. }
  1323. return NULL;
  1324. }
  1325. /** return a malloc'd string representing a unique name for a new track */
  1326. char *
  1327. Timeline::get_unique_track_name ( const char *name )
  1328. {
  1329. char pat[256];
  1330. strcpy( pat, name );
  1331. for ( int i = 1; track_by_name( pat ); ++i )
  1332. snprintf( pat, sizeof( pat ), "%s.%d", name, i );
  1333. return strdup( pat );
  1334. }
  1335. /**********/
  1336. /* Public */
  1337. /**********/
  1338. /** return the current length of the timeline, which is arrived at by
  1339. * calculating the end frame of the rightmost audio region on an
  1340. * active audio sequence. Control_Points, etc. do not factor into this
  1341. * calcaulation. */
  1342. nframes_t
  1343. Timeline::length ( void ) const
  1344. {
  1345. nframes_t l = 0;
  1346. for ( int i = tracks->children(); i--; )
  1347. l = max( l, ((Track*)tracks->child( i ))->sequence()->length() );
  1348. // adjust_panzoomer();
  1349. return l;
  1350. }
  1351. /** set horizontal scroll postion to absolute pixel coordinate /X/ */
  1352. void
  1353. Timeline::xposition ( int X )
  1354. {
  1355. xoffset = x_to_ts( X );
  1356. int dx = ts_to_x( _old_xposition ) - ts_to_x( xoffset );
  1357. if ( dx )
  1358. damage( FL_DAMAGE_SCROLL );
  1359. }
  1360. /** zoom in by one zoom step */
  1361. void
  1362. Timeline::zoom_in ( void )
  1363. {
  1364. panzoomer->zoom_in();
  1365. }
  1366. /** zoom out by one zoom step */
  1367. void
  1368. Timeline::zoom_out ( void )
  1369. {
  1370. panzoomer->zoom_out();
  1371. }
  1372. /** zoom the display to show /secs/ seconds per screen */
  1373. void
  1374. Timeline::zoom ( float secs )
  1375. {
  1376. const int sw = tracks->w() - Track::width();
  1377. int fpp = (int)((secs * sample_rate()) / sw);
  1378. int p = 0;
  1379. while ( 1 << p < fpp ) p++;
  1380. panzoomer->zoom( p );
  1381. redraw();
  1382. }
  1383. /** fit the zoom to the current length of the timeline (subject to nearest power of two) */
  1384. void
  1385. Timeline::zoom_fit ( void )
  1386. {
  1387. xposition( 0 );
  1388. if ( length() )
  1389. zoom( length() / (float)sample_rate() );
  1390. else
  1391. zoom( 60 );
  1392. }
  1393. /** add /track/ to the timeline */
  1394. void
  1395. Timeline::add_track ( Track *track )
  1396. {
  1397. DMESSAGE( "added new track to the timeline" );
  1398. wrlock();
  1399. engine->lock();
  1400. tracks->add( track );
  1401. // update_track_order();
  1402. engine->unlock();
  1403. unlock();
  1404. /* FIXME: why is this necessary? doesn't the above add do DAMAGE_CHILD? */
  1405. redraw();
  1406. }
  1407. void
  1408. Timeline::insert_track ( Track *track, int n )
  1409. {
  1410. if ( n > tracks->children() || n < 0 )
  1411. return;
  1412. wrlock();
  1413. engine->lock();
  1414. tracks->insert( *track, n );
  1415. update_track_order();
  1416. tracks->redraw();
  1417. engine->unlock();
  1418. unlock();
  1419. /* FIXME: why is this necessary? doesn't the above add do DAMAGE_CHILD? */
  1420. // redraw();
  1421. }
  1422. static
  1423. bool
  1424. compare_tracks ( Track *a, Track *b )
  1425. {
  1426. return *a < *b;
  1427. }
  1428. void
  1429. Timeline::apply_track_order ( void )
  1430. {
  1431. wrlock();
  1432. engine->lock();
  1433. std::list<Track*> tl;
  1434. for ( int i = 0; i < tracks->children(); i++ )
  1435. tl.push_back( (Track*)tracks->child( i ) );
  1436. tl.sort(compare_tracks);
  1437. Fl_Widget **a = const_cast<Fl_Widget**>(tracks->array());
  1438. int j = 0;
  1439. for ( std::list<Track*>::const_iterator i = tl.begin();
  1440. i != tl.end();
  1441. i++, j++ )
  1442. a[j] = *i;
  1443. update_track_order();
  1444. engine->unlock();
  1445. unlock();
  1446. }
  1447. void
  1448. Timeline::update_track_order ( void )
  1449. {
  1450. for ( int i = 0; i < tracks->children(); i++ )
  1451. ((Track*)tracks->child( i ))->row( i );
  1452. }
  1453. int
  1454. Timeline::find_track ( const Track *track ) const
  1455. {
  1456. return tracks->find( *track );
  1457. }
  1458. void
  1459. Timeline::move_track_up ( Track *track )
  1460. {
  1461. insert_track( track, find_track( track ) - 1 );
  1462. }
  1463. void
  1464. Timeline::move_track_down ( Track *track )
  1465. {
  1466. insert_track( track, find_track( track ) + 2 );
  1467. }
  1468. /** remove /track/ from the timeline */
  1469. void
  1470. Timeline::remove_track ( Track *track )
  1471. {
  1472. DMESSAGE( "removed track from the timeline" );
  1473. wrlock();
  1474. engine->lock();
  1475. /* FIXME: what to do about track contents? */
  1476. tracks->remove( track );
  1477. update_track_order();
  1478. engine->unlock();
  1479. unlock();
  1480. /* FIXME: why is this necessary? doesn't the above add do DAMAGE_CHILD? */
  1481. redraw();
  1482. }
  1483. /************/
  1484. /* Commands */
  1485. /************/
  1486. void
  1487. Timeline::command_quit ( )
  1488. {
  1489. Project::close();
  1490. command_save();
  1491. while ( Fl::first_window() ) Fl::first_window()->hide();
  1492. }
  1493. bool
  1494. Timeline::command_load ( const char *name, const char *display_name )
  1495. {
  1496. if ( ! name )
  1497. return false;
  1498. int r = Project::open( name );
  1499. if ( r < 0 )
  1500. {
  1501. const char *s = Project::errstr( r );
  1502. fl_alert( "Could not open project \"%s\":\n\n\t%s", name, s );
  1503. return false;
  1504. }
  1505. Project::set_name ( display_name ? display_name : name );
  1506. apply_track_order();
  1507. return true;
  1508. }
  1509. bool
  1510. Timeline::command_save ( )
  1511. {
  1512. tle->save_options();
  1513. return true;
  1514. }
  1515. bool
  1516. Timeline::command_new ( const char *name, const char *display_name )
  1517. {
  1518. bool b = Project::create( name, NULL );
  1519. Project::set_name ( display_name );
  1520. /* FIXME: there's other stuff that needs to be done here! */
  1521. /* tle->update_menu(); */
  1522. /* tle->main_window->redraw(); */
  1523. return b;
  1524. }
  1525. const char *
  1526. Timeline::session_manager_name ( void )
  1527. {
  1528. return nsm_get_session_manager_name( nsm );
  1529. }
  1530. /*******/
  1531. /* OSC */
  1532. /*******/
  1533. const double OSC_INTERVAL = 0.2f;
  1534. void
  1535. Timeline::check_osc ( void * v )
  1536. {
  1537. ((Timeline*)v)->osc->check();
  1538. Fl::repeat_timeout( OSC_INTERVAL, &Timeline::check_osc, v );
  1539. }
  1540. int
  1541. Timeline::init_osc ( const char *osc_port )
  1542. {
  1543. osc = new OSC::Endpoint();
  1544. if ( int r = osc->init( LO_UDP, osc_port ) )
  1545. return r;
  1546. osc->owner = this;
  1547. printf( "OSC=%s\n", osc->url() );
  1548. osc->add_method( "/non/hello", "ssss", &Timeline::osc_non_hello, osc, "" );
  1549. // osc->start();
  1550. /* poll so we can keep OSC handlers running in the GUI thread and avoid extra sync */
  1551. Fl::add_timeout( OSC_INTERVAL, &Timeline::check_osc, this );
  1552. osc->peer_scan_complete_callback( &Timeline::handle_peer_scan_complete, this );
  1553. if ( ! osc_thread )
  1554. {
  1555. osc_thread = new OSC_Thread();
  1556. osc_thread->start();
  1557. }
  1558. return 0;
  1559. }
  1560. int
  1561. Timeline::osc_non_hello ( const char *path, const char *, lo_arg **argv, int argc, lo_message, void * )
  1562. {
  1563. OSC_DMSG();
  1564. if ( argc >= 4 )
  1565. {
  1566. const char *url = &argv[0]->s;
  1567. const char *name = &argv[1]->s;
  1568. const char *version = &argv[2]->s;
  1569. const char *id = &argv[3]->s;
  1570. MESSAGE( "Discovered NON peer %s (%s) @ %s with ID \"%s\"", name, version, url, id );
  1571. MESSAGE( "Registering Signals" );
  1572. timeline->osc->hello( url );
  1573. return 0;
  1574. }
  1575. return -1;
  1576. }
  1577. void
  1578. Timeline::reply_to_finger ( lo_message msg )
  1579. {
  1580. int argc = lo_message_get_argc( msg );
  1581. lo_arg **argv = lo_message_get_argv( msg );
  1582. if ( argc < 1 )
  1583. return;
  1584. lo_address reply = lo_address_new_from_url( &argv[0]->s );
  1585. osc->send( reply,
  1586. "/non/hello",
  1587. osc->url(),
  1588. APP_NAME,
  1589. VERSION,
  1590. instance_name );
  1591. osc->hello( &argv[0]->s );
  1592. lo_address_free( reply );
  1593. }
  1594. void
  1595. Timeline::handle_peer_scan_complete ( void *o )
  1596. {
  1597. ((Timeline*)o)->connect_osc();
  1598. }
  1599. void
  1600. Timeline::connect_osc ( void )
  1601. {
  1602. /* try to (re)connect OSC signals */
  1603. for ( int i = tracks->children(); i-- ; )
  1604. {
  1605. Track *t = (Track*)tracks->child( i );
  1606. for ( int j = t->control->children(); j--; )
  1607. {
  1608. Control_Sequence *c = (Control_Sequence*)t->control->child( j );
  1609. c->connect_osc();
  1610. }
  1611. }
  1612. }
  1613. void
  1614. Timeline::discover_peers ( void )
  1615. {
  1616. if ( nsm_is_active( nsm ) )
  1617. {
  1618. lo_message m = lo_message_new();
  1619. lo_message_add( m, "sssss",
  1620. "/non/hello",
  1621. osc->url(),
  1622. APP_NAME,
  1623. VERSION,
  1624. instance_name );
  1625. nsm_send_broadcast( nsm, m );
  1626. }
  1627. }
  1628. /* runs in the OSC thread... */
  1629. void
  1630. Timeline::process_osc ( void )
  1631. {
  1632. THREAD_ASSERT( OSC );
  1633. rdlock();
  1634. /* reconnect OSC signals */
  1635. for ( int i = tracks->children(); i-- ; )
  1636. {
  1637. Track *t = (Track*)tracks->child( i );
  1638. if ( t->control )
  1639. {
  1640. for ( int j = t->control->children(); j--; )
  1641. {
  1642. Control_Sequence *c = (Control_Sequence*)t->control->child( j );
  1643. c->process_osc();
  1644. }
  1645. }
  1646. }
  1647. unlock();
  1648. }