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