1 /*
2 * Author: Harry van Haaren 2013
3 * harryhaaren@gmail.com
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "goptions.hxx"
20
21 #include "eventhandler.hxx"
22
23 #include <FL/Fl_Button.H>
24
25 #include "controller/binding.hxx"
26 #include "controller/controller.hxx"
27 #include "controller/genericmidi.hxx"
28
29 #include "event.hxx"
30
31 #include "gui.hxx"
32 extern Gui* gui;
33
addControllerUiDsp(OptionsWindow * self,GenericMIDI * c)34 static void addControllerUiDsp(OptionsWindow* self, GenericMIDI* c)
35 {
36 // add the controller to the UI
37 int x, y, w, h;
38 self->tabs->client_area( x, y, w, h, 25 );
39 self->controllers.push_back( new ControllerUI( x, y, w, h, c->getName().c_str(), c->getID() ) );
40
41 // store the pointer to the options window: needed to make remove button work
42 self->controllers.back()->optionsWindow = self;
43
44 LUPPP_NOTE("Added controller %s, ID %i", c->getName().c_str(), c->getID() );
45
46 // add widget before "add" button
47 self->tabs->insert( *self->controllers.back()->widget, self->addGroup );
48
49 // tell the ControllerUI to add the bindings from this Controller*
50 self->controllers.back()->setAuthor( c->getAuthor() );
51 self->controllers.back()->setLink( c->getEmail() );
52 self->controllers.back()->addBindings( c );
53
54 self->tabs->redraw();
55
56 // send to DSP side
57 EventControllerInstance e(c);
58 writeToDspRingbuffer( &e );
59 }
60
updateAuthorCB(Fl_Widget * w,void * data)61 static void updateAuthorCB(Fl_Widget* w, void* data)
62 {
63 ControllerUI* c = (ControllerUI*)data;
64 const char* s = fl_input( "Author: ", "" );
65 if ( s ) {
66 c->setAuthor( s );
67 }
68 }
69
updateLinkCB(Fl_Widget * w,void * data)70 static void updateLinkCB(Fl_Widget* w, void* data)
71 {
72 ControllerUI* c = (ControllerUI*)data;
73
74 stringstream str;
75 str << "xdg-open ";
76
77 // add http:// if its not in the string
78 std::string l = c->getLink();
79 if ( ( l.find("http") ) == std::string::npos )
80 str << " http://";
81
82 str << l;
83
84 int ret = system( str.str().c_str() );
85 /* if it fails it fails... (void) to mute clang warning */
86 (void)ret;
87 }
88
writeBindEnable(Fl_Widget * w,void * data)89 static void writeBindEnable(Fl_Widget* w, void* data)
90 {
91 OptionsWindow* o = (OptionsWindow*) data;
92 //LUPPP_NOTE("MIDI bind mode");
93
94 Avtk::LightButton* l = (Avtk::LightButton*)w;
95 l->value( !l->value() );
96
97 int controllerID = -1; // waste?
98 EventControllerBindingEnable e( controllerID, l->value() );
99 writeToDspRingbuffer( &e );
100 }
101
removeControllerCB(Fl_Widget * w,void * data)102 static void removeControllerCB(Fl_Widget* w, void* data)
103 {
104 ControllerUI* self = (ControllerUI*)data;
105
106 // Remove UI tab for that controller
107 // should return "tabs" from OptionsWindow
108 self->optionsWindow->tabs->remove( self->widget );
109 self->optionsWindow->tabs->redraw();
110
111 // FIXME: confirm action here?
112
113 //LUPPP_NOTE("Removing controllerID %i", self->controllerID );
114 EventControllerInstanceRemove e( self->controllerID );
115 writeToDspRingbuffer( &e );
116
117 delete self;
118 }
119
addNewController(Fl_Widget * w,void * ud)120 static void addNewController(Fl_Widget* w, void* ud)
121 {
122 OptionsWindow* self = (OptionsWindow*)ud;
123 LUPPP_NOTE("%s","ADD Controller cb");
124
125 GenericMIDI* c = 0;
126
127 const char* name = fl_input( "Controller name: ", "" );
128 if ( name ) {
129 c = new GenericMIDI( 0, name);
130 } else {
131 return;
132 }
133
134
135 if ( c->status() == Controller::CONTROLLER_OK ) {
136 addControllerUiDsp( self, c );
137 } else {
138 LUPPP_ERROR("Controller initialization failed!");
139 }
140 }
141
142
143
selectLoadController(Fl_Widget * w,void * data)144 static void selectLoadController(Fl_Widget* w, void* data)
145 {
146 OptionsWindow* self = (OptionsWindow*)data;
147
148 // FIXME: refactor
149 string path;
150 Fl_Native_File_Chooser fnfc;
151 fnfc.title("Pick a controller definition");
152 fnfc.type(Fl_Native_File_Chooser::BROWSE_FILE);
153 fnfc.filter("Controllers\t*.ctlr");
154
155 stringstream s;
156 s << getenv("HOME") << "/.config/openAV/luppp/controllers/";
157 fnfc.directory( s.str().c_str() ); // default directory to use
158 // Show native chooser
159 switch ( fnfc.show() ) {
160 case -1: /*printf("ERROR: %s\n", fnfc.errmsg());*/
161 break; // ERROR
162 case 1: /*printf("CANCEL\n"); */
163 break; // CANCEL
164 default:
165 //printf("Loading controller at %s\n", fnfc.filename());
166 path = fnfc.filename();
167 break;
168 }
169
170 if ( strcmp( path.c_str(), "" ) == 0 )
171 return;
172
173 //LUPPP_NOTE("%s","ADD Controller cb");
174 GenericMIDI* c = new GenericMIDI( path );
175
176 if ( c->status() == Controller::CONTROLLER_OK ) {
177 addControllerUiDsp( self, c );
178 } else {
179 LUPPP_ERROR("Controller initialization failed!");
180 }
181 }
182
183
184
writeControllerFile(Fl_Widget * w,void * data)185 static void writeControllerFile(Fl_Widget* w, void* data)
186 {
187 ControllerUI* c = (ControllerUI*)data;
188
189 LUPPP_NOTE("Writing controller %li, %s ID %i .ctlr to disk", c, c->name.c_str(), c->controllerID );
190
191 // Set the Controller details in diskWriter, so it write it pretty
192 gui->getDiskWriter()->writeControllerInfo( CONTROLLER_NAME , c->name );
193 gui->getDiskWriter()->writeControllerInfo( CONTROLLER_AUTHOR, c->getAuthor());
194 gui->getDiskWriter()->writeControllerInfo( CONTROLLER_LINK , c->getLink() );
195
196 EventControllerInstanceGetToWrite e( c->controllerID );
197 writeToDspRingbuffer( &e );
198 }
199
200
deleteBindingFromController(Fl_Widget * w,void * ud)201 static void deleteBindingFromController(Fl_Widget* w, void* ud)
202 {
203 ControllerUI* self = (ControllerUI*)ud;
204 stringstream s;
205 s << w->label();
206 int tmp;
207 s >> tmp;
208 LUPPP_NOTE("CtlrID %i: Deleting binding with ID %i", self->controllerID, tmp );
209
210 EventControllerBindingRemove e( self->controllerID, tmp );
211 writeToDspRingbuffer( &e );
212
213 // remove "this" widget (and its parent Pack) from the list of MIDI bindings:
214 self->bindingsPack->remove( w->parent() );
215 self->bindingsPack->redraw();
216 self->scroll->redraw();
217 }
218
219
ControllerUI(int x,int y,int w,int h,std::string n,int ID)220 ControllerUI::ControllerUI(int x, int y, int w, int h, std::string n, int ID)
221 {
222 name = n;
223
224 widget = new Fl_Group( x, y, w, h, name.c_str());
225 {
226 // author / link
227 authorLabel = new Avtk::Button( x + 5, y + 5, 190, 25, "Author?" );
228 linkLabel = new Avtk::Button( x + 7+ w/2, y + 5, 190, 25, "Link?" );
229
230 authorLabel->label("Author?");
231 authorLabel->label("Link?");
232
233 authorLabel->callback( updateAuthorCB, this );
234 linkLabel->callback( updateLinkCB, this );
235
236 // binding / target
237 targetLabelStat = new Fl_Box(x + 100,y + 32, 75, 25,"Target: ");
238 targetLabel = new Fl_Box(x + 140,y + 32, 200, 25,"");
239 bindEnable = new Avtk::LightButton(x + 5, y + 32, 100, 25, "Bind Enable");
240
241 writeControllerBtn = new Avtk::Button( x + 5, y + h - 27, 100, 25, "Save" );
242 removeController = new Avtk::Button( x + 110, y + h - 27, 100, 25, "Remove");
243
244 scroll = new Fl_Scroll( x + 5, y + 82, 395, 265 );
245 {
246 bindingsPack = new Fl_Pack( x + 5, y + 82, w - 15, 270-10);
247 bindingsPack->end();
248 bindingsPack->spacing( 2 );
249 bindingsPack->box(FL_DOWN_FRAME);
250 }
251 scroll->resizable( bindingsPack );
252 scroll->box( FL_DOWN_FRAME );
253 scroll->type( Fl_Scroll::VERTICAL_ALWAYS );
254 scroll->end();
255
256 widget->resizable( scroll );
257 }
258 widget->end();
259
260 widget->redraw();
261
262 // save the controller ID this ControllerUI represents
263 controllerID = ID;
264 LUPPP_NOTE("Controller %li ID on create %i", this, controllerID );
265
266 //ctlrButton->callback( selectLoadController );
267 bindEnable->callback( writeBindEnable, this );
268 removeController->callback( removeControllerCB, this );
269 writeControllerBtn->callback( writeControllerFile, this );
270 }
271
setTarget(const char * n)272 void OptionsWindow::setTarget(const char* n)
273 {
274 for(unsigned int i = 0; i < controllers.size(); i++ ) {
275 controllers.at(i)->setTarget( n );
276 }
277 }
278
setTarget(const char * n)279 void ControllerUI::setTarget( const char* n )
280 {
281 target = n;
282 targetLabel->label( target.c_str() );
283 targetLabel->redraw();
284 }
285
setAuthor(std::string a)286 void ControllerUI::setAuthor(std::string a)
287 {
288 author = a;
289 authorLabel->label( author.c_str() );
290 authorLabel->redraw();
291 }
292
setLink(std::string e)293 void ControllerUI::setLink(std::string e)
294 {
295 link = e;
296 linkLabel->label( link.c_str() );
297 linkLabel->redraw();
298 }
299
setBindEnable(bool b)300 void ControllerUI::setBindEnable( bool b )
301 {
302 bindEnable->value( b );
303 }
304
addBinding(Binding * b)305 void ControllerUI::addBinding( Binding* b )
306 {
307 if ( b->action != EVENT_NULL ) {
308 // add individual bindings as they're made
309 const char* tmp = Event::getPrettyName( b->action );
310 if ( !tmp ) {
311 #ifdef DEBUG_MIDI
312 LUPPP_NOTE("new binding, action string returned NULL, action number %i ", b->action );
313 #endif
314 return;
315 }
316 } else {
317 LUPPP_WARN("new binding, action: == EVENT_NULL" );
318 return;
319 }
320
321 // push the bindingID onto the vector
322 bindingID.push_back( b->ID );
323
324 // create a horizontal pack, add that to the bindingsPack
325 Fl_Pack* tmp = new Fl_Pack( 35, 35, 25, 25);
326 {
327 tmp->type( Fl_Pack::HORIZONTAL );
328 tmp->spacing( 2 );
329
330 stringstream s;
331
332 if ( b->action == MASTER_VOL )
333 s << "Master Volume";
334 else if ( false )
335 s << "stuff";
336 else
337 s << Event::getPrettyName( b->action );
338
339
340
341 if (b->track != -2)
342 s << " Track:" << b->track + 1;
343
344 if (b->scene != -1)
345 s << " Scene:" << b->scene + 1;
346
347 if ( b->send == Event::SEND_POSTFADER )
348 s << " Post-fader Send";
349 if ( b->send == Event::SEND_XSIDE )
350 s << " Sidechain Crossfade";
351 if ( b->send == Event::SEND_KEY )
352 s << " Sidechain Key";
353
354 if ( b->active != -1) {
355 if ( b->active == true )
356 s << " On";
357 if ( b->active == false )
358 s << " Off";
359 }
360
361 // button to remove a binding, uses bindingsID vector to get unique number
362 stringstream id;
363 id << b->ID;
364 char *str = strdup(id.str().c_str());
365 Fl_Button* but = new Fl_Button(35, 35, 25, 25, str );
366 if(!but)
367 free(str);
368 but->callback( deleteBindingFromController, this );
369 but->redraw();
370
371 Fl_Box* b = new Fl_Box(35, 35, 400, 25, strdup(s.str().c_str()) );
372 b->align( FL_ALIGN_LEFT | FL_ALIGN_INSIDE );
373 b->redraw();
374 }
375 tmp->end();
376
377 // push the binding to the UI pack, *and* store its binding ID in the same
378 // array element in the bindingID vector. Used to remove bindings
379 bindingsPack->add( tmp );
380
381 bindingsPack->resize( bindingsPack->x(),bindingsPack->y(),bindingsPack->w(),bindingsPack->children() * 36 );
382 bindingsPack->redraw();
383 scroll->redraw();
384
385 //LUPPP_NOTE("binding size %i %i", bindingsPack->w(), bindingsPack->h() );
386 }
387
addBindings(GenericMIDI * c)388 void ControllerUI::addBindings( GenericMIDI* c )
389 {
390 std::vector<Binding*> bindingVector= c->getMidiToAction();
391
392 for(unsigned int i = 0; i < bindingVector.size(); i++ ) {
393 addBinding( bindingVector.at(i) );
394 }
395 }
396
~ControllerUI()397 ControllerUI::~ControllerUI()
398 {
399
400 // free all binding resources here:
401
402
403 delete authorLabel;
404 delete linkLabel;
405
406 delete targetLabel;
407 delete targetLabelStat;
408 delete bindEnable;
409 delete removeController;
410 delete writeControllerBtn;
411 }
412
OptionsWindow()413 OptionsWindow::OptionsWindow()
414 {
415 window = new Fl_Double_Window(400,400,"Options");
416
417 window->set_non_modal();
418
419 tabs = new Fl_Tabs(0, 0, 400, 400);
420
421 window->resizable( tabs );
422
423 int x, y, w, h;
424 tabs->client_area( x, y, w, h, 25 );
425
426 addGroup = new Fl_Group(x,y,w,h,"Add");
427 {
428 newButton = new Avtk::Button( x+2, y+2, w-4, 30, "New Generic MIDI");
429 loadButton = new Avtk::Button( x+2, y+2+32, w-4, 30, "Load Generic MIDI");
430 }
431 addGroup->end();
432 tabs->end();
433
434 newButton ->callback( addNewController, this );
435 loadButton->callback( selectLoadController, this );
436
437 window->end();
438 }
439
~OptionsWindow()440 OptionsWindow::~OptionsWindow()
441 {
442 delete newButton;
443 delete loadButton;
444 delete addGroup;
445 delete tabs;
446 delete window;
447 }
448
show()449 void OptionsWindow::show()
450 {
451 window->show();
452 }
453
hide()454 void OptionsWindow::hide()
455 {
456 window->hide();
457 }
458
getControllerUI(int id)459 ControllerUI* OptionsWindow::getControllerUI(int id)
460 {
461 for(unsigned int i = 0; i < controllers.size(); i++ ) {
462 if ( controllers.at(i)->controllerID == id ) {
463 return controllers.at(i);
464 }
465 }
466
467 // error: controller not found!
468 return 0;
469 }
470