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.

553 lines
14KB

  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. #include "Sequence.H"
  19. #include "Timeline.H"
  20. #include <FL/fl_draw.H>
  21. #include "Track.H"
  22. #include "FL/event_name.H"
  23. #include "Transport.H" // for locate()
  24. #include "../FL/Boxtypes.H"
  25. #include "const.h"
  26. #include "util/debug.h"
  27. using namespace std;
  28. queue <Sequence_Widget *> Sequence::_delete_queue;
  29. Sequence::Sequence ( Track *track ) : Fl_Widget( 0, 0, 0, 0 ), Loggable( true )
  30. {
  31. init();
  32. _track = track;
  33. // log_create();
  34. }
  35. Sequence::Sequence ( int X, int Y, int W, int H ) : Fl_Widget( X, Y, W, H ), Loggable( false )
  36. {
  37. init();
  38. }
  39. void
  40. Sequence::init ( void )
  41. {
  42. _track = NULL;
  43. _name = NULL;
  44. box( FL_DOWN_BOX );
  45. color( FL_BACKGROUND_COLOR );
  46. align( FL_ALIGN_LEFT );
  47. // clear_visible_focus();
  48. }
  49. Sequence::~Sequence ( )
  50. {
  51. DMESSAGE( "destroying sequence" );
  52. if ( _name )
  53. free( _name );
  54. if ( _widgets.size() )
  55. FATAL( "programming error: leaf destructor must call Sequence::clear()!" );
  56. }
  57. /** remove all widgets from this sequence */
  58. void
  59. Sequence::clear ( void )
  60. {
  61. Loggable::block_start();
  62. for ( std::list <Sequence_Widget*>::iterator i = _widgets.begin();
  63. i != _widgets.end(); ++i )
  64. {
  65. Sequence_Widget *w = *i;
  66. *i = NULL;
  67. delete w;
  68. }
  69. _widgets.clear();
  70. Loggable::block_end();
  71. }
  72. /** given screen pixel coordinate X, return an absolute frame offset into this sequence */
  73. nframes_t
  74. Sequence::x_to_offset ( int X )
  75. {
  76. return timeline->xoffset + timeline->x_to_ts( X - x() );
  77. }
  78. /** sort the widgets in this sequence by position */
  79. void
  80. Sequence::sort ( void )
  81. {
  82. _widgets.sort( Sequence_Widget::sort_func );
  83. }
  84. /** return a pointer to the widget that /r/ overlaps, or NULL if none. */
  85. Sequence_Widget *
  86. Sequence::overlaps ( Sequence_Widget *r )
  87. {
  88. for ( list <Sequence_Widget *>::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ )
  89. {
  90. if ( *i == r ) continue;
  91. if ( (*i)->overlaps( r ) )
  92. return *i;
  93. }
  94. return NULL;
  95. }
  96. void
  97. Sequence::handle_widget_change ( nframes_t start, nframes_t length )
  98. {
  99. // timeline->update_length( start + length );
  100. }
  101. Sequence_Widget *
  102. Sequence::widget_at ( nframes_t ts, int Y )
  103. {
  104. for ( list <Sequence_Widget *>::const_reverse_iterator r = _widgets.rbegin(); r != _widgets.rend(); ++r )
  105. if ( ts >= (*r)->start() && ts <= (*r)->start() + (*r)->length()
  106. && Y >= (*r)->y() && Y <= (*r)->y() + (*r)->h() )
  107. return (*r);
  108. return NULL;
  109. }
  110. /** return a pointer to the widget under the current mouse event, or
  111. * NULL if no widget intersects the event coordinates */
  112. Sequence_Widget *
  113. Sequence::event_widget ( void )
  114. {
  115. nframes_t ets = timeline->xoffset + timeline->x_to_ts( Fl::event_x() - x() );
  116. return widget_at( ets, Fl::event_y() );
  117. }
  118. void
  119. Sequence::add ( Sequence_Widget *r )
  120. {
  121. // Logger _log( this );
  122. if ( r->sequence() )
  123. {
  124. r->redraw();
  125. r->sequence()->remove( r );
  126. // r->track()->redraw();
  127. }
  128. timeline->wrlock();
  129. r->sequence( this );
  130. _widgets.push_back( r );
  131. sort();
  132. timeline->unlock();
  133. handle_widget_change( r->start(), r->length() );
  134. }
  135. void
  136. Sequence::remove ( Sequence_Widget *r )
  137. {
  138. timeline->wrlock();
  139. _widgets.remove( r );
  140. timeline->unlock();
  141. handle_widget_change( r->start(), r->length() );
  142. }
  143. static nframes_t
  144. abs_diff ( nframes_t n1, nframes_t n2 )
  145. {
  146. return n1 > n2 ? n1 - n2 : n2 - n1;
  147. }
  148. /** snap widget /r/ to nearest edge */
  149. void
  150. Sequence::snap ( Sequence_Widget *r )
  151. {
  152. const int snap_pixels = 10;
  153. const nframes_t snap_frames = timeline->x_to_ts( snap_pixels );
  154. /* snap to other widgets */
  155. if ( Timeline::snap_magnetic )
  156. {
  157. const int rx1 = r->start();
  158. const int rx2 = r->start() + r->length();
  159. for ( list <Sequence_Widget*>::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ )
  160. {
  161. const Sequence_Widget *w = (*i);
  162. if ( w == r )
  163. continue;
  164. const int wx1 = w->start();
  165. const int wx2 = w->start() + w->length();
  166. if ( abs_diff( rx1, wx2 ) < snap_frames )
  167. {
  168. r->start( w->start() + w->length() + 1 );
  169. return;
  170. }
  171. if ( abs_diff( rx2, wx1 ) < snap_frames )
  172. {
  173. r->start( ( w->start() - r->length() ) - 1 );
  174. return;
  175. }
  176. }
  177. }
  178. nframes_t f = r->start();
  179. /* snap to beat/bar lines */
  180. if ( timeline->nearest_line( &f ) )
  181. r->start( f );
  182. }
  183. void
  184. Sequence::draw ( void )
  185. {
  186. if ( ! fl_not_clipped( x(), y(), w(), h() ) )
  187. return;
  188. fl_push_clip( x(), y(), w(), h() );
  189. /* draw the box with the ends cut off. */
  190. draw_box( box(), x() - Fl::box_dx( box() ) - 1, y(), w() + Fl::box_dw( box() ) + 2, h(), color() );
  191. int X, Y, W, H;
  192. fl_clip_box( x(), y(), w(), h(), X, Y, W, H );
  193. /* if ( Sequence_Widget::pushed() && Sequence_Widget::pushed()->sequence() == this ) */
  194. /* { */
  195. /* /\* make sure the Sequence_Widget::pushed widget is above all others *\/ */
  196. /* remove( Sequence_Widget::pushed() ); */
  197. /* add( Sequence_Widget::pushed() ); */
  198. /* } */
  199. // printf( "track::draw %d,%d %dx%d\n", X,Y,W,H );
  200. timeline->draw_measure_lines( X, Y, W, H, color() );
  201. for ( list <Sequence_Widget *>::const_iterator r = _widgets.begin(); r != _widgets.end(); ++r )
  202. (*r)->draw_box();
  203. for ( list <Sequence_Widget *>::const_iterator r = _widgets.begin(); r != _widgets.end(); ++r )
  204. (*r)->draw();
  205. fl_pop_clip();
  206. }
  207. #include "FL/test_press.H"
  208. int
  209. Sequence::handle ( int m )
  210. {
  211. /* if ( m != FL_NO_EVENT ) */
  212. /* DMESSAGE( "%s", event_name( m ) ); */
  213. switch ( m )
  214. {
  215. case FL_KEYBOARD:
  216. case FL_SHORTCUT:
  217. if ( Fl::test_shortcut( FL_CTRL + FL_Right ) )
  218. {
  219. transport->locate( next( transport->frame ) );
  220. return 1;
  221. }
  222. else if ( Fl::test_shortcut( FL_CTRL + FL_Left ) )
  223. {
  224. transport->locate( prev( transport->frame ) );
  225. return 1;
  226. }
  227. else if ( Fl::test_shortcut( FL_CTRL + ' ' ) )
  228. {
  229. Sequence_Widget *r = widget_at( transport->frame, y() );
  230. if ( r )
  231. {
  232. if ( r->selected() )
  233. r->deselect();
  234. else
  235. r->select();
  236. }
  237. }
  238. else
  239. {
  240. switch ( Fl::event_key() )
  241. {
  242. case FL_Left:
  243. case FL_Right:
  244. case FL_Up:
  245. case FL_Down:
  246. /* this is a hack to override FLTK's use of arrow keys for
  247. * focus navigation */
  248. return timeline->handle_scroll( m );
  249. default:
  250. break;
  251. }
  252. }
  253. if ( Sequence_Widget::belowmouse() )
  254. return Sequence_Widget::belowmouse()->dispatch( m );
  255. case FL_NO_EVENT:
  256. /* garbage from overlay window */
  257. return 0;
  258. case FL_FOCUS:
  259. Fl_Widget::handle( m );
  260. redraw();
  261. return 1;
  262. case FL_UNFOCUS:
  263. Fl_Widget::handle( m );
  264. redraw();
  265. return 1;
  266. case FL_LEAVE:
  267. // DMESSAGE( "leave" );
  268. fl_cursor( FL_CURSOR_DEFAULT );
  269. Fl_Widget::handle( m );
  270. return 1;
  271. case FL_DND_DRAG:
  272. return 1;
  273. case FL_ENTER:
  274. // DMESSAGE( "enter" );
  275. if ( Sequence_Widget::pushed() )
  276. {
  277. if ( Sequence_Widget::pushed()->sequence()->class_name() == class_name() )
  278. {
  279. /* accept objects dragged from other sequences of this type */
  280. add( Sequence_Widget::pushed() );
  281. redraw();
  282. fl_cursor( FL_CURSOR_MOVE );
  283. }
  284. else
  285. fl_cursor( (Fl_Cursor)1 );
  286. }
  287. else
  288. if ( ! event_widget() )
  289. fl_cursor( cursor() );
  290. Fl_Widget::handle( m );
  291. return 1;
  292. case FL_DND_ENTER:
  293. case FL_DND_LEAVE:
  294. case FL_DND_RELEASE:
  295. return 1;
  296. case FL_MOVE:
  297. {
  298. Sequence_Widget *r = event_widget();
  299. if ( r != Sequence_Widget::belowmouse() )
  300. {
  301. if ( Sequence_Widget::belowmouse() )
  302. Sequence_Widget::belowmouse()->handle( FL_LEAVE );
  303. Sequence_Widget::belowmouse( r );
  304. if ( r )
  305. r->handle( FL_ENTER );
  306. }
  307. return 1;
  308. }
  309. default:
  310. {
  311. Sequence_Widget *r = Sequence_Widget::pushed() ? Sequence_Widget::pushed() : event_widget();
  312. /* if ( this == Fl::focus() ) */
  313. /* DMESSAGE( "Sequence widget = %p", r ); */
  314. if ( r )
  315. {
  316. int retval = r->dispatch( m );
  317. /* DMESSAGE( "retval = %d", retval ); */
  318. if ( m == FL_PUSH )
  319. take_focus();
  320. if ( retval )
  321. {
  322. if ( m == FL_PUSH )
  323. {
  324. if ( Sequence_Widget::pushed() )
  325. Sequence_Widget::pushed()->handle( FL_UNFOCUS );
  326. Sequence_Widget::pushed( r );
  327. r->handle( FL_FOCUS );
  328. }
  329. else if ( m == FL_RELEASE )
  330. Sequence_Widget::pushed( NULL );
  331. }
  332. Loggable::block_start();
  333. while ( _delete_queue.size() )
  334. {
  335. Sequence_Widget *t = _delete_queue.front();
  336. _delete_queue.pop();
  337. if ( Sequence_Widget::pushed() == t )
  338. Sequence_Widget::pushed( NULL );
  339. if ( Sequence_Widget::belowmouse() == t )
  340. {
  341. Sequence_Widget::belowmouse()->handle( FL_LEAVE );
  342. Sequence_Widget::belowmouse( NULL );
  343. }
  344. delete t;
  345. }
  346. Loggable::block_end();
  347. if ( m == FL_PUSH )
  348. return 1;
  349. else
  350. return retval;
  351. }
  352. else
  353. {
  354. if ( test_press( FL_BUTTON1 ) )
  355. {
  356. /* traditional selection model */
  357. Sequence_Widget::select_none();
  358. }
  359. return Fl_Widget::handle( m );
  360. }
  361. }
  362. }
  363. }
  364. /**********/
  365. /* Public */
  366. /**********/
  367. /** calculate the length of this sequence by looking at the end of the
  368. * least widget it contains */
  369. /** return the length in frames of this sequence calculated from the
  370. * right edge of the rightmost widget */
  371. nframes_t
  372. Sequence::length ( void ) const
  373. {
  374. nframes_t l = 0;
  375. for ( list <Sequence_Widget *>::const_iterator r = _widgets.begin(); r != _widgets.end(); ++r )
  376. l = max( l, (*r)->start() + (*r)->length() );
  377. return l;
  378. }
  379. /** return the location of the next widget from frame /from/ */
  380. nframes_t
  381. Sequence::next ( nframes_t from ) const
  382. {
  383. for ( list <Sequence_Widget*>::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ )
  384. if ( (*i)->start() > from )
  385. return (*i)->start();
  386. if ( _widgets.size() )
  387. return _widgets.back()->start();
  388. else
  389. return 0;
  390. }
  391. /** return the location of the next widget from frame /from/ */
  392. nframes_t
  393. Sequence::prev ( nframes_t from ) const
  394. {
  395. for ( list <Sequence_Widget*>::const_reverse_iterator i = _widgets.rbegin(); i != _widgets.rend(); i++ )
  396. if ( (*i)->start() < from )
  397. return (*i)->start();
  398. if ( _widgets.size() )
  399. return _widgets.front()->start();
  400. else
  401. return 0;
  402. }
  403. /** delete all selected widgets in this sequence */
  404. void
  405. Sequence::remove_selected ( void )
  406. {
  407. Loggable::block_start();
  408. for ( list <Sequence_Widget *>::iterator r = _widgets.begin(); r != _widgets.end(); )
  409. if ( (*r)->selected() )
  410. {
  411. Sequence_Widget *t = *r;
  412. _widgets.erase( r++ );
  413. delete t;
  414. }
  415. else
  416. ++r;
  417. Loggable::block_end();
  418. }
  419. /** select all widgets intersecting with the range defined by the
  420. * pixel coordinates X through W */
  421. void
  422. Sequence::select_range ( int X, int W )
  423. {
  424. nframes_t sts = x_to_offset( X );
  425. nframes_t ets = sts + timeline->x_to_ts( W );
  426. for ( list <Sequence_Widget *>::const_reverse_iterator r = _widgets.rbegin(); r != _widgets.rend(); ++r )
  427. if ( ! ( (*r)->start() > ets || (*r)->start() + (*r)->length() < sts ) )
  428. (*r)->select();
  429. }