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