1 /*
2  *  tracker/TrackerShortCuts.cpp
3  *
4  *  Copyright 2009 Peter Barth
5  *
6  *  This file is part of Milkytracker.
7  *
8  *  Milkytracker is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Milkytracker is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 /*
24  *  TrackerShortCuts.cpp
25  *  MilkyTracker
26  *
27  *  Created by Peter Barth on Thu May 19 2005.
28  *
29  */
30 
31 #include "Tracker.h"
32 #include "ControlIDs.h"
33 #include "Screen.h"
34 #include "Event.h"
35 #include "PlayerController.h"
36 #include "PlayerLogic.h"
37 #include "RecorderLogic.h"
38 
39 #include "Container.h"
40 #include "ListBox.h"
41 #include "PatternEditorControl.h"
42 
43 #include "ModuleEditor.h"
44 #include "TrackerConfig.h"
45 
46 #include "InputControlListener.h"
47 #include "SectionInstruments.h"
48 #include "SectionTranspose.h"
49 #include "SectionDiskMenu.h"
50 
sendNoteDown(mp_sint32 note,pp_int32 volume)51 void Tracker::sendNoteDown(mp_sint32 note, pp_int32 volume/* = -1*/)
52 {
53 	if (volume != -1 && volume > 255)
54 		volume = 255;
55 
56 	// Volume here is between 0 to 255, but don't forget to make the volume FT2 compatible (0..64)
57 	inputControlListener->sendNote(note | InputControlListener::KEY_PRESS,
58 								   volume == -1 ? -1 : (signed)PatternTools::normalizeVol(volume));
59 }
60 
sendNoteUp(mp_sint32 note)61 void Tracker::sendNoteUp(mp_sint32 note)
62 {
63 	// bit 16 indicates key release
64 	inputControlListener->sendNote(note | InputControlListener::KEY_RELEASE);
65 }
66 
processShortcuts(PPEvent * event)67 void Tracker::processShortcuts(PPEvent* event)
68 {
69 	if (processMessageBoxShortcuts(event))
70 		return;
71 
72 	switch (editMode)
73 	{
74 		case EditModeMilkyTracker:
75 			processShortcutsMilkyTracker(event);
76 			break;
77 
78 		case EditModeFastTracker:
79 			processShortcutsFastTracker(event);
80 			break;
81 
82 		default:
83 			ASSERT(false);
84 	}
85 }
86 
processShortcutsMilkyTracker(PPEvent * event)87 void Tracker::processShortcutsMilkyTracker(PPEvent* event)
88 {
89 	if (event->getID() == eKeyDown)
90 	{
91 		pp_uint16 keyCode = *((pp_uint16*)event->getDataPtr());
92 		pp_uint16 scanCode = *(((pp_uint16*)event->getDataPtr())+1);
93 		switch (keyCode)
94 		{
95 			case VK_F1:
96 			case VK_F2:
97 			case VK_F3:
98 			case VK_F4:
99 			case VK_F5:
100 			case VK_F6:
101 			case VK_F7:
102 			case VK_F8:
103 			case VK_F9:
104 			case VK_F10:
105 			case VK_F11:
106 			case VK_F12:
107 			{
108 				if (::getKeyModifier())
109 					goto processBindings;
110 
111 				if (static_cast<PPControl*>(getPatternEditorControl()) != screen->getFocusedControl())
112 				{
113 					getPatternEditorControl()->dispatchEvent(event);
114 				}
115 				break;
116 			}
117 
118 			default:
119 			{
120 processBindings:
121 				pp_int32 keyModifier = ::getKeyModifier();
122 				bool res = executeBinding(eventKeyDownBindings, keyCode);
123 
124 				if (res && !isActiveEditing())
125 					event->cancel();
126 
127 				if (res || keyModifier)
128 					break;
129 
130 				if (editMode == EditModeMilkyTracker)
131 				{
132 					if (sectionDiskMenu->isFileBrowserVisible() &&
133 						sectionDiskMenu->fileBrowserHasFocus())
134 						break;
135 				}
136 
137 				PatternEditorControl* patternEditorControl = getPatternEditorControl();
138 
139 				// translate key to note
140 				pp_int32 note = patternEditorControl->ScanCodeToNote(scanCode);
141 
142 				recorderLogic->sendNoteDownToPatternEditor(event, note, patternEditorControl);
143 				break;
144 			}
145 
146 		}
147 	}
148 	else if (event->getID() == eKeyUp)
149 	{
150 		pp_uint16 keyCode = *((pp_uint16*)event->getDataPtr());
151 		pp_uint16 scanCode = *(((pp_uint16*)event->getDataPtr())+1);
152 
153 		switch (keyCode)
154 		{
155 			case VK_SPACE:
156 			{
157 				playerLogic->finishTraceAndRowPlay();
158 				break;
159 			}
160 
161 			default:
162 			{
163 				PatternEditorControl* patternEditorControl = getPatternEditorControl();
164 
165 				pp_int32 note = patternEditorControl->ScanCodeToNote(scanCode);
166 
167 				recorderLogic->sendNoteUpToPatternEditor(event, note, patternEditorControl);
168 			}
169 		}
170 
171 	}
172 }
173 
selectNextOrder(bool wrap)174 void Tracker::selectNextOrder(bool wrap/* = false*/)
175 {
176 	if (wrap && listBoxOrderList->isLastEntry())
177 	{
178 		setOrderListIndex(0);
179 		return;
180 	}
181 
182 	pp_uint16 vk[3] = {VK_DOWN, 0, 0};
183 	PPEvent e(eKeyDown, &vk, sizeof(vk));
184 	listBoxOrderList->dispatchEvent(&e);
185 }
186 
selectPreviousOrder(bool wrap)187 void Tracker::selectPreviousOrder(bool wrap/* = false*/)
188 {
189 	if (wrap && listBoxOrderList->isFirstEntry())
190 	{
191 		setOrderListIndex(listBoxOrderList->getNumItems()-1);
192 		return;
193 	}
194 
195 	pp_uint16 vk[3] = {VK_UP, 0, 0};
196 	PPEvent e(eKeyDown, &vk, sizeof(vk));
197 	listBoxOrderList->dispatchEvent(&e);
198 }
199 
selectNextInstrument()200 void Tracker::selectNextInstrument()
201 {
202 	pp_uint16 vk[3] = {VK_DOWN, 0, 0};
203 	PPEvent e(eKeyDown, &vk, sizeof(vk));
204 	listBoxInstruments->dispatchEvent(&e);
205 }
206 
selectPreviousInstrument()207 void Tracker::selectPreviousInstrument()
208 {
209 	pp_uint16 vk[3] = {VK_UP, 0, 0};
210 	PPEvent e(eKeyDown, &vk, sizeof(vk));
211 	listBoxInstruments->dispatchEvent(&e);
212 }
213 
214 ///////////////////////////////////////////////////////////////////////////////
215 // The Fasttracker II compatibility mode is really just a big hack, because
216 // MilkyTracker uses focus handling on most editable controls while FT2 doesn't
217 // ----------------------------------------------------------------------------
218 // 1. a defined set of keys always are always routed to to the pattern editor
219 // 2. If record mode is ON all keyboard events are also routed to pattern editor
220 //    (no matter if it can handle them or not)
221 // 3. Keys are not routed into any other control except for editing
222 ///////////////////////////////////////////////////////////////////////////////
processShortcutsFastTracker(PPEvent * event)223 void Tracker::processShortcutsFastTracker(PPEvent* event)
224 {
225 	if (isActiveEditing())
226 		return;
227 
228 	/*if (screen->getFocusedControl() != static_cast<PPControl*>(getPatternEditorControl()))
229 	{
230 		screen->setFocus(getPatternEditorControl());
231 		screen->paintControl(getPatternEditorControl());
232 	}*/
233 
234 	if (event->getID() == eKeyDown)
235 	{
236 		pp_uint16 keyCode = *((pp_uint16*)event->getDataPtr());
237 		pp_uint16 scanCode = *(((pp_uint16*)event->getDataPtr())+1);
238 
239 		switch (scanCode)
240 		{
241 			case SC_WTF:
242 				if (!::getKeyModifier() || ::getKeyModifier() == KeyModifierSHIFT)
243 				{
244 					getPatternEditorControl()->dispatchEvent(event);
245 					event->cancel();
246 					keyCode = 0;
247 				}
248 				break;
249 
250 			// Place cursor in channel
251 			case SC_Q:
252 			case SC_W:
253 			case SC_E:
254 			case SC_R:
255 			case SC_T:
256 			case SC_Z:
257 			case SC_U:
258 			case SC_I:
259 			case SC_A:
260 			case SC_S:
261 			case SC_D:
262 			case SC_F:
263 			case SC_G:
264 			case SC_H:
265 			case SC_J:
266 			case SC_K:
267 				if (screen->getModalControl())
268 					break;
269 
270 				if (::getKeyModifier() == KeyModifierALT)
271 				{
272 					getPatternEditorControl()->dispatchEvent(event);
273 					event->cancel();
274 					keyCode = 0;
275 				}
276 				break;
277 
278 			// Increment/decrement instrument
279 			case SC_SS:
280 			case SC_TICK:
281 				if (screen->getModalControl())
282 					break;
283 
284 				if (::getKeyModifier() == KeyModifierCTRL ||
285 					::getKeyModifier() == (KeyModifierSHIFT|KeyModifierCTRL))
286 				{
287 					getPatternEditorControl()->dispatchEvent(event);
288 					event->cancel();
289 					keyCode = 0;
290 				}
291 				break;
292 		}
293 
294 		switch (keyCode)
295 		{
296 			case VK_SPACE:
297 			{
298 				if (screen->getModalControl())
299 					break;
300 
301 				if (::getKeyModifier())
302 					goto processOthers;
303 
304 				if (playerController->isPlaying() || playerController->isPlayingPattern())
305 				{
306 					playerLogic->stopSong();
307 					event->cancel();
308 					break;
309 				}
310 
311 				playerLogic->stopSong();
312 
313 				eventKeyDownBinding_ToggleFT2Edit();
314 
315 				event->cancel();
316 				break;
317 			}
318 
319 			// Those are the key combinations which are always routed to pattern editor control as long
320 			// as we're in Fasttracker editing mode
321 			case VK_ALT:
322 			case VK_SHIFT:
323 			case VK_CONTROL:
324 				if (screen->getModalControl())
325 					break;
326 
327 				getPatternEditorControl()->dispatchEvent(event);
328 				event->cancel();
329 				break;
330 
331 			// Transpose (regardless of modifers)
332 			case VK_F1:
333 			case VK_F2:
334 			case VK_F7:
335 			case VK_F8:
336 			case VK_F9:
337 			case VK_F10:
338 			case VK_F11:
339 			case VK_F12:
340 				processShortcutsMilkyTracker(event);
341 				break;
342 
343 			// Cut copy paste
344 			case VK_F3:
345 			case VK_F4:
346 			case VK_F5:
347 			case VK_F6:
348 				// Global meaning here
349 				if (::getKeyModifier())
350 				{
351 					getPatternEditorControl()->dispatchEvent(event);
352 					event->cancel();
353 					break;
354 				}
355 				processShortcutsMilkyTracker(event);
356 				break;
357 
358 			// Some special keys always going to the pattern editor (like undo, redo, mute etc.)
359 			case 'A':
360 			case 'C':
361 			case 'V':
362 			case 'X':
363 			case 'Z':
364 			case 'Y':
365 				if (screen->getModalControl())
366 				{
367 					// those seem to be piano keys, they're used in some
368 					// modal dialogs for instrument preview playback
369 					if (!::getKeyModifier())
370 						goto processOthers;
371 
372 					break;
373 				}
374 
375 				if (::getKeyModifier() == (KeyModifierCTRL|KeyModifierALT))
376 				{
377 					getPatternEditorControl()->dispatchEvent(event);
378 					event->cancel();
379 				}
380 				else goto processOthers;
381 				break;
382 
383 			case 'I':
384 				if (screen->getModalControl())
385 					break;
386 
387 				if (::getKeyModifier() == KeyModifierSHIFT)
388 				{
389 					getPatternEditorControl()->dispatchEvent(event);
390 					event->cancel();
391 				}
392 				else goto processOthers;
393 				break;
394 
395 			case 'M':
396 				if (screen->getModalControl())
397 					break;
398 
399 				if (::getKeyModifier() == KeyModifierSHIFT ||
400 					::getKeyModifier() == (KeyModifierSHIFT|KeyModifierCTRL))
401 				{
402 					getPatternEditorControl()->dispatchEvent(event);
403 					event->cancel();
404 				}
405 				else goto processOthers;
406 				break;
407 
408 			case VK_UP:
409 			case VK_DOWN:
410 			case VK_LEFT:
411 			case VK_RIGHT:
412 			case VK_HOME:
413 			case VK_END:
414 			case VK_PRIOR:
415 			case VK_NEXT:
416 				if (screen->getModalControl())
417 					break;
418 
419 				if (!::getKeyModifier() ||
420 					::getKeyModifier() == KeyModifierALT ||
421 					::getKeyModifier() == (KeyModifierSHIFT|KeyModifierALT))
422 				{
423 					getPatternEditorControl()->dispatchEvent(event);
424 					event->cancel();
425 				}
426 				else if (::getKeyModifier() == KeyModifierSHIFT)
427 				{
428 					switch (keyCode)
429 					{
430 						// Select instrument using Shift+Up/Down
431 						case VK_UP:
432 						case VK_DOWN:
433 						case VK_NEXT:
434 						case VK_PRIOR:
435 							listBoxInstruments->dispatchEvent(event);
436 							event->cancel();
437 							break;
438 
439 						// Select new order using Shift+Left/Right
440 						case VK_LEFT:
441 						{
442 							selectPreviousOrder();
443 							event->cancel();
444 							break;
445 						}
446 						case VK_RIGHT:
447 						{
448 							selectNextOrder();
449 							event->cancel();
450 							break;
451 						}
452 					}
453 				}
454 				else if (::getKeyModifier() == (KeyModifierSHIFT|KeyModifierCTRL))
455 				{
456 					switch (keyCode)
457 					{
458 						// Select sample using Shift+Alt+Up/Down
459 						case VK_UP:
460 						case VK_DOWN:
461 						case VK_NEXT:
462 						case VK_PRIOR:
463 							listBoxSamples->dispatchEvent(event);
464 							event->cancel();
465 							break;
466 					}
467 				}
468 				else if (::getKeyModifier() == KeyModifierCTRL)
469 				{
470 					switch (keyCode)
471 					{
472 						// Select pattern using Ctrl+Left/Right
473 						case VK_LEFT:
474 							eventKeyDownBinding_PreviousPattern();
475 							event->cancel();
476 							break;
477 
478 						case VK_RIGHT:
479 							eventKeyDownBinding_NextPattern();
480 							event->cancel();
481 							break;
482 					}
483 				}
484 				goto processOthers;
485 				break;
486 
487 			case VK_TAB:
488 				if (screen->getModalControl())
489 					break;
490 
491 				getPatternEditorControl()->dispatchEvent(event);
492 				event->cancel();
493 				break;
494 
495 			default:
496 processOthers:
497 				processShortcutsMilkyTracker(event);
498 
499 				if (screen->getModalControl())
500 					break;
501 
502 				if (recorderLogic->getRecordMode())
503 				{
504 					getPatternEditorControl()->dispatchEvent(event);
505 					event->cancel();
506 				}
507 				// if recordMode is false and focus is on pattern editor
508 				// we need to cancel the event in order to prevent it
509 				// from going into the pattern editor
510 				else if (screen->getFocusedControl() == static_cast<PPControl*>(getPatternEditorControl()))
511 				{
512 					event->cancel();
513 				}
514 		}
515 	}
516 	else if (event->getID() == eKeyChar)
517 	{
518 		if (recorderLogic->getRecordMode())
519 		{
520 			getPatternEditorControl()->dispatchEvent(event);
521 			event->cancel();
522 		}
523 		// if recordMode is false and focus is on pattern editor
524 		// we need to cancel the event in order to prevent it
525 		// from going into the pattern editor
526 		else if (screen->getFocusedControl() == static_cast<PPControl*>(getPatternEditorControl()))
527 		{
528 			event->cancel();
529 		}
530 	}
531 	else if (event->getID() == eKeyUp)
532 	{
533 		pp_uint16 keyCode = *((pp_uint16*)event->getDataPtr());
534 		//pp_uint16 scanCode = *(((pp_uint16*)event->getDataPtr())+1);
535 
536 		switch (keyCode)
537 		{
538 			// Those are the keykombinations which are always routed to pattern editor control as long
539 			// as we're in Fasttracker editing mode
540 			case VK_ALT:
541 			case VK_SHIFT:
542 			case VK_CONTROL:
543 				if (screen->getModalControl())
544 					break;
545 
546 				getPatternEditorControl()->dispatchEvent(event);
547 				event->cancel();
548 				break;
549 
550 			default:
551 				processShortcutsMilkyTracker(event);
552 
553 				if (screen->getModalControl())
554 					/*break;*/return;
555 
556 				if (recorderLogic->getRecordMode())
557 				{
558 					getPatternEditorControl()->dispatchEvent(event);
559 					event->cancel();
560 				}
561 				// if recordMode is false and focus is on pattern editor
562 				// we need to cancel the event in order to prevent it
563 				// from going into the pattern editor
564 				else if (screen->getFocusedControl() == static_cast<PPControl*>(getPatternEditorControl()))
565 				{
566 					event->cancel();
567 				}
568 		}
569 	}
570 }
571 
572 
switchEditMode(EditModes mode)573 void Tracker::switchEditMode(EditModes mode)
574 {
575 	bool b = (mode == EditModeMilkyTracker);
576 
577 	PPContainer* container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_MENU));
578 	ASSERT(container);
579 
580 	// Assign keyboard bindings
581 	getPatternEditorControl()->setShowFocus(b);
582 	listBoxInstruments->setShowFocus(b);
583 	listBoxSamples->setShowFocus(b);
584 	listBoxOrderList->setShowFocus(b);
585 	sectionDiskMenu->setFileBrowserShowFocus(b);
586 	sectionDiskMenu->setCycleFilenames(b);
587 	container = static_cast<PPContainer*>(screen->getControlByID(CONTAINER_ABOUT));
588 	ASSERT(container);
589 	static_cast<PPListBox*>(container->getControlByID(LISTBOX_SONGTITLE))->setShowFocus(b);
590 
591 	if (b)
592 	{
593 		eventKeyDownBindings = eventKeyDownBindingsMilkyTracker;
594 		screen->setFocus(listBoxInstruments, false);
595 	}
596 	else
597 	{
598 		eventKeyDownBindings = eventKeyDownBindingsFastTracker;
599 		recorderLogic->setRecordMode(true);
600 		eventKeyDownBinding_ToggleFT2Edit();
601 	}
602 
603 	getPatternEditorControl()->switchEditMode(mode);
604 
605 	editMode = mode;
606 }
607 
608 // Process messagebox shortcuts (RETURN & ESC)
609 // the modal dialogs only appear to be modal, we're still getting
610 // keyboard events here in case a modal dialog box appears
611 // this is the handler which allows for esc + return handling in case
612 // of a modal dialog
simulateMouseClickEvent(PPControl * ctrl)613 static void simulateMouseClickEvent(PPControl* ctrl)
614 {
615 	PPPoint p = ctrl->getLocation();
616 	p.x+=ctrl->getSize().width >> 1;
617 	p.y+=ctrl->getSize().height >> 1;
618 
619 	PPEvent e1(eLMouseDown, &p, sizeof(PPPoint));
620 	PPEvent e2(eLMouseUp, &p, sizeof(PPPoint));
621 
622 	ctrl->dispatchEvent(&e1);
623 	ctrl->dispatchEvent(&e2);
624 }
625 
processMessageBoxShortcuts(PPEvent * event)626 bool Tracker::processMessageBoxShortcuts(PPEvent* event)
627 {
628 	PPControl* ctrl = screen->getModalControl();
629 
630 	if (ctrl == NULL || !ctrl->isContainer() || (event->getID() != eKeyDown
631 			&& event->getID() != eKeyChar))
632 		return false;
633 
634 	PPSimpleVector<PPControl>& controls = static_cast<PPContainer*>(ctrl)->getControls();
635 
636 	pp_int32 i;
637 	// if dialog contains list (list can also be an edit field btw.)
638 	// and something is being edited in that list we don't simulate
639 	// yes/no/cancel button presses
640 	for (i = 0; i < controls.size(); i++)
641 	{
642 		PPControl* ctrl = controls.get(i);
643 		if (ctrl->isListBox() && static_cast<PPListBox*>(ctrl)->isEditing())
644 			return true;
645 	}
646 
647 	// iterate over controls in dialog and see whether we can find
648 	// yes/no/cancel buttons
649 	// if that's the case we simulate mouse button press
650 	if (event->getID() == eKeyDown)
651 	{
652 		pp_uint16 keyCode = *((pp_uint16*)event->getDataPtr());
653 
654 		for (i = 0; i < controls.size(); i++)
655 		{
656 			PPControl* ctrl = controls.get(i);
657 			switch (ctrl->getID())
658 			{
659 				case PP_MESSAGEBOX_BUTTON_YES:
660 					if (keyCode == VK_RETURN)
661 					{
662 						simulateMouseClickEvent(ctrl);
663 						return true;
664 					}
665 					break;
666 
667 				case PP_MESSAGEBOX_BUTTON_CANCEL:
668 				case PP_MESSAGEBOX_BUTTON_NO:
669 					if (keyCode == VK_ESCAPE)
670 					{
671 						simulateMouseClickEvent(ctrl);
672 						return true;
673 					}
674 					break;
675 			}
676 		}
677 	}
678 
679 	return false;
680 }
681