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.

544 lines
13KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2009 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 main mixer group. It contains and manages Mixer_Strips. */
  19. #include "const.h"
  20. #include "Mixer.H"
  21. #include "Mixer_Strip.H"
  22. #include <FL/Fl_Pack.H>
  23. #include <FL/Fl_Scroll.H>
  24. #include <FL/Fl_Menu_Bar.H>
  25. #include <FL/fl_ask.H>
  26. #include <FL/Fl.H>
  27. #include <FL/Fl_File_Chooser.H>
  28. #include "New_Project_Dialog.H"
  29. #include "Engine/Engine.H"
  30. #include "FL/Fl_Flowpack.H"
  31. #include "Project.H"
  32. #include "FL/Fl_Menu_Settings.H"
  33. #include "About_Dialog.H"
  34. #include "util/file.h"
  35. #include <string.h>
  36. #include "debug.h"
  37. #include "FL/color_scheme.H"
  38. const double STATUS_UPDATE_FREQ = 0.2f;
  39. extern char *user_config_dir;
  40. #include "util/debug.h"
  41. static void update_cb( void *v ) {
  42. Fl::repeat_timeout( STATUS_UPDATE_FREQ, update_cb, v );
  43. ((Mixer*)v)->update();
  44. }
  45. void Mixer::cb_menu(Fl_Widget* o) {
  46. Fl_Menu_Bar *menu = (Fl_Menu_Bar*)o;
  47. /* const Fl_Menu_Item *mi = &menu->menu()[menu->value()]; */
  48. char picked[256];
  49. // const char *picked = menu->text();
  50. menu->item_pathname( picked, sizeof( picked ) );
  51. if (! strcmp( picked, "&Project/&New") )
  52. {
  53. DMESSAGE( "New project" );
  54. const char *templates[] = { "Default", NULL };
  55. char *default_path;
  56. char *selected_template;
  57. read_line( user_config_dir, "default_path", &default_path );
  58. char *path = new_project_chooser( templates, &default_path, &selected_template );
  59. if ( path )
  60. {
  61. if ( ! Project::create( path, selected_template ) )
  62. fl_alert( "Error creating project!" );
  63. free( path );
  64. free( selected_template );
  65. }
  66. if ( default_path )
  67. {
  68. write_line( user_config_dir, "default_path", default_path );
  69. free( default_path );
  70. }
  71. }
  72. else if (! strcmp( picked, "&Project/&Open" ) )
  73. {
  74. char *path = NULL;
  75. // read_line( user_config_dir, "default_path", &path );
  76. const char *name = fl_dir_chooser( "Open Project", path, NULL );
  77. free( path );
  78. mixer->hide();
  79. if ( int err = Project::open( name ) )
  80. {
  81. fl_alert( "Error opening project: %s", Project::errstr( err ) );
  82. mixer->show();
  83. }
  84. mixer->show();
  85. }
  86. else if (! strcmp( picked, "&Project/&Save" ) )
  87. {
  88. command_save();
  89. }
  90. else if (! strcmp( picked, "&Project/&Quit") )
  91. {
  92. command_quit();
  93. }
  94. else if ( !strcmp( picked, "&Mixer/&Add Strip" ) )
  95. {
  96. command_add_strip();
  97. }
  98. else if ( !strcmp( picked, "&Mixer/Add &N Strips" ) )
  99. {
  100. const char *s = fl_input( "Enter number of strips to add" );
  101. if ( s )
  102. {
  103. for ( int i = atoi( s ); i > 0; i-- )
  104. command_add_strip();
  105. }
  106. }
  107. else if (! strcmp( picked, "&Mixer/&Rows/One") )
  108. {
  109. rows( 1 );
  110. }
  111. else if (! strcmp( picked, "&Mixer/&Rows/Two") )
  112. {
  113. rows( 2 );
  114. }
  115. else if (! strcmp( picked, "&Mixer/&Rows/Three") )
  116. {
  117. rows( 3 );
  118. }
  119. else if (! strcmp( picked, "&Options/&Display/&Style/&Default") )
  120. {
  121. Fl::scheme( "plastic" );
  122. }
  123. else if (! strcmp( picked, "&Options/&Display/&Style/&Flat") )
  124. {
  125. Fl::scheme( "gtk+" );
  126. }
  127. else if (! strcmp( picked, "&Options/&Display/&Colors/&System") )
  128. {
  129. color_scheme( "system" );
  130. }
  131. else if (! strcmp( picked, "&Options/&Display/&Colors/&Dark") )
  132. {
  133. color_scheme( "dark" );
  134. }
  135. else if (! strcmp( picked, "&Options/&Display/&Colors/&Very Dark") )
  136. {
  137. color_scheme( "very dark" );
  138. }
  139. else if (! strcmp( picked, "&Options/&Display/&Colors/&Light") )
  140. {
  141. color_scheme( "light" );
  142. }
  143. else if ( ! strcmp( picked, "&Help/&About" ) )
  144. {
  145. About_Dialog ab;
  146. ab.run();
  147. }
  148. else if ( !strcmp( picked, "&Help/&Manual" ))
  149. {
  150. char *pat;
  151. asprintf( &pat, "file://%s%s.html", DOCUMENT_PATH, "MANUAL.html" );
  152. open_url( pat );
  153. free( pat );
  154. }
  155. }
  156. void Mixer::cb_menu(Fl_Widget* o, void* v) {
  157. ((Mixer*)(v))->cb_menu(o);
  158. }
  159. Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) :
  160. Fl_Group( X, Y, W, H, L )
  161. {
  162. get_system_colors();
  163. Fl::scheme( "plastic" );
  164. color_scheme( "dark" );
  165. _rows = 1;
  166. box( FL_NO_BOX );
  167. labelsize( 96 );
  168. { Fl_Menu_Bar *o = menubar = new Fl_Menu_Bar( X, Y, W, 24 );
  169. o->add( "&Project/&New" );
  170. o->add( "&Project/&Open" );
  171. o->add( "&Project/&Save", FL_CTRL + 's', 0, 0 );
  172. o->add( "&Project/&Quit", FL_CTRL + 'q', 0, 0 );
  173. o->add( "&Mixer/&Add Strip", 'a', 0, 0 );
  174. o->add( "&Mixer/Add &N Strips" );
  175. o->add( "&Mixer/&Rows/One", '1', 0, 0 );
  176. o->add( "&Mixer/&Rows/Two", '2', 0, 0 );
  177. o->add( "&Mixer/&Rows/Three", '3', 0, 0 );
  178. o->add( "_&Options/&Display/&Style/&Default", 0, 0, 0, FL_MENU_RADIO | FL_MENU_VALUE );
  179. o->add( "_&Options/&Display/&Style/&Flat", 0, 0, 0, FL_MENU_RADIO );
  180. o->add( "_&Options/&Display/&Colors/&Dark", 0, 0, 0, FL_MENU_RADIO | FL_MENU_VALUE );
  181. o->add( "_&Options/&Display/&Colors/&Very Dark", 0, 0, 0, FL_MENU_RADIO );
  182. o->add( "_&Options/&Display/&Colors/&Light", 0, 0, 0, FL_MENU_RADIO );
  183. o->add( "_&Options/&Display/&Colors/&System", 0, 0, 0, FL_MENU_RADIO );
  184. o->add( "&Help/&Manual" );
  185. o->add( "&Help/&About" );
  186. o->callback( cb_menu, this );
  187. }
  188. { Fl_Scroll *o = scroll = new Fl_Scroll( X, Y + 24, W, H - 24 );
  189. o->box( FL_NO_BOX );
  190. // o->type( Fl_Scroll::HORIZONTAL_ALWAYS );
  191. // o->box( Fl_Scroll::BOTH );
  192. {
  193. Fl_Flowpack *o = mixer_strips = new Fl_Flowpack( X, Y + 24, W, H - 18 - 24 );
  194. label( "Non-Mixer" );
  195. align( (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE) );
  196. o->box( FL_NO_BOX );
  197. o->type( Fl_Pack::HORIZONTAL );
  198. o->hspacing( 2 );
  199. o->vspacing( 2 );
  200. o->end();
  201. Fl_Group::current()->resizable( o );
  202. }
  203. o->end();
  204. Fl_Group::current()->resizable( o );
  205. }
  206. end();
  207. // Fl::add_timeout( STATUS_UPDATE_FREQ, update_cb, this );
  208. load_options();
  209. }
  210. Mixer::~Mixer ( )
  211. {
  212. DMESSAGE( "Destroying mixer" );
  213. save_options();
  214. /* FIXME: teardown */
  215. mixer_strips->clear();
  216. }
  217. void Mixer::resize ( int X, int Y, int W, int H )
  218. {
  219. Fl_Group::resize( X, Y, W, H );
  220. mixer_strips->resize( X, Y + 24, W, H - 18 - 24 );
  221. scroll->resize( X, Y + 24, W, H - 24 );
  222. rows( _rows );
  223. }
  224. void Mixer::add ( Mixer_Strip *ms )
  225. {
  226. MESSAGE( "Add mixer strip \"%s\"", ms->name() );
  227. mixer_strips->add( ms );
  228. ms->take_focus();
  229. rows( _rows );
  230. scroll->redraw();
  231. }
  232. void
  233. Mixer::quit ( void )
  234. {
  235. /* TODO: save project? */
  236. while ( Fl::first_window() ) Fl::first_window()->hide();
  237. }
  238. void
  239. Mixer::insert ( Mixer_Strip *ms, Mixer_Strip *before )
  240. {
  241. mixer_strips->remove( ms );
  242. mixer_strips->insert( *ms, before );
  243. scroll->redraw();
  244. }
  245. void
  246. Mixer::insert ( Mixer_Strip *ms, int i )
  247. {
  248. Mixer_Strip *before = (Mixer_Strip*)mixer_strips->child( i );
  249. insert( ms, before);
  250. }
  251. void
  252. Mixer::move_left ( Mixer_Strip *ms )
  253. {
  254. int i = mixer_strips->find( ms );
  255. if ( i > 0 )
  256. insert( ms, i - 1 );
  257. }
  258. void
  259. Mixer::move_right ( Mixer_Strip *ms )
  260. {
  261. int i = mixer_strips->find( ms );
  262. if ( i < mixer_strips->children() - 1 )
  263. insert( ms, i + 2 );
  264. }
  265. void Mixer::remove ( Mixer_Strip *ms )
  266. {
  267. MESSAGE( "Remove mixer strip \"%s\"", ms->name() );
  268. mixer_strips->remove( ms );
  269. parent()->redraw();
  270. }
  271. bool
  272. Mixer::contains ( Mixer_Strip *ms )
  273. {
  274. return ms->parent() == mixer_strips;
  275. }
  276. void
  277. Mixer::rows ( int n )
  278. {
  279. int sh;
  280. if ( n > 1 )
  281. {
  282. sh = (scroll->h() / n) - (mixer_strips->vspacing() * (n - 1));
  283. mixer_strips->flow( true );
  284. }
  285. else
  286. {
  287. sh = (scroll->h() - 18) / n;
  288. mixer_strips->flow( false );
  289. }
  290. if ( sh < Mixer_Strip::min_h() )
  291. {
  292. rows( ( scroll->h() - 18 ) / Mixer_Strip::min_h() );
  293. return;
  294. }
  295. int tw = 0;
  296. for ( int i = 0; i < mixer_strips->children(); ++i )
  297. {
  298. Mixer_Strip *t = (Mixer_Strip*)mixer_strips->child( i );
  299. t->size( t->w(), sh );
  300. tw += t->w() + mixer_strips->hspacing();
  301. }
  302. if ( n > 1 )
  303. mixer_strips->size( scroll->w() - 18, mixer_strips->h() );
  304. else
  305. mixer_strips->size( tw, mixer_strips->h() );
  306. _rows = n;
  307. scroll->redraw();
  308. }
  309. void Mixer::update ( void )
  310. {
  311. THREAD_ASSERT( UI );
  312. for ( int i = mixer_strips->children(); i--; )
  313. {
  314. ((Mixer_Strip*)mixer_strips->child( i ))->update();
  315. }
  316. // redraw();
  317. }
  318. /** retrun a pointer to the track named /name/, or NULL if no track is named /name/ */
  319. Mixer_Strip *
  320. Mixer::track_by_name ( const char *name )
  321. {
  322. for ( int i = mixer_strips->children(); i-- ; )
  323. {
  324. Mixer_Strip *t = (Mixer_Strip*)mixer_strips->child( i );
  325. if ( ! strcmp( name, t->name() ) )
  326. return t;
  327. }
  328. return NULL;
  329. }
  330. /** return a malloc'd string representing a unique name for a new track */
  331. char *
  332. Mixer::get_unique_track_name ( const char *name )
  333. {
  334. char pat[256];
  335. strcpy( pat, name );
  336. for ( int i = 1; track_by_name( pat ); ++i )
  337. snprintf( pat, sizeof( pat ), "%s.%d", name, i );
  338. return strdup( pat );
  339. }
  340. void
  341. Mixer::snapshot ( void )
  342. {
  343. for ( int i = 0; i < mixer_strips->children(); ++i )
  344. ((Mixer_Strip*)mixer_strips->child( i ))->log_children();
  345. }
  346. void
  347. Mixer::new_strip ( void )
  348. {
  349. add( new Mixer_Strip( get_unique_track_name( "Unnamed" ) ) );
  350. }
  351. bool
  352. Mixer::save ( void )
  353. {
  354. MESSAGE( "Saving state" );
  355. Loggable::snapshot_callback( &Mixer::snapshot, this );
  356. Loggable::snapshot( "snapshot" );
  357. return true;
  358. }
  359. static const char options_filename[] = "options";
  360. void
  361. Mixer::load_options ( void )
  362. {
  363. // save options
  364. char *path;
  365. asprintf( &path, "%s/options", user_config_dir );
  366. ((Fl_Menu_Settings*)menubar)->load( menubar->find_item( "&Options" ), path );
  367. free( path );
  368. }
  369. void
  370. Mixer::save_options ( void )
  371. {
  372. char *path;
  373. asprintf( &path, "%s/%s", user_config_dir, options_filename );
  374. ((Fl_Menu_Settings*)menubar)->dump( menubar->find_item( "&Options" ), path );
  375. free( path );
  376. }
  377. int
  378. Mixer::handle ( int m )
  379. {
  380. if ( Fl_Group::handle( m ) )
  381. return 1;
  382. switch ( m )
  383. {
  384. case FL_ENTER:
  385. case FL_LEAVE:
  386. return 1;
  387. }
  388. return 0;
  389. }
  390. /************/
  391. /* Commands */
  392. /************/
  393. bool
  394. Mixer::command_save ( void )
  395. {
  396. return Project::save();
  397. }
  398. bool
  399. Mixer::command_load ( const char *path, const char *display_name )
  400. {
  401. if ( int err = Project::open( path ) )
  402. {
  403. // fl_alert( "Error opening project specified on commandline: %s", Project::errstr( err ) );
  404. return false;
  405. }
  406. if ( display_name )
  407. Project::name( display_name );
  408. return true;
  409. }
  410. bool
  411. Mixer::command_new ( const char *path, const char *display_name )
  412. {
  413. if ( ! Project::create( path, "" ) )
  414. return false;
  415. if ( display_name )
  416. Project::name( display_name );
  417. return true;
  418. // fl_alert( "Error creating project!" );
  419. }
  420. void
  421. Mixer::command_quit ( void )
  422. {
  423. quit();
  424. }
  425. /* */
  426. void
  427. Mixer::command_add_strip ( void )
  428. {
  429. new_strip();
  430. }