1 /*
2  *  tracker/PatternEditor.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  *  PatternEditor.cpp
25  *  MilkyTracker
26  *
27  *  Created by Peter Barth on 16.11.07.
28  *
29  */
30 
31 #include "PatternEditor.h"
32 #include "XModule.h"
33 #include "PatternTools.h"
34 #include "SimpleVector.h"
35 
backup()36 void PatternEditor::Selection::backup()
37 {
38 	startCopy = start;
39 	endCopy = end;
40 	copyValid = true;
41 }
42 
restore()43 void PatternEditor::Selection::restore()
44 {
45 	start = startCopy;
46 	end = endCopy;
47 	copyValid = false;
48 }
49 
50 PatternEditor::ClipBoard* PatternEditor::ClipBoard::instances[PatternEditor::ClipBoardTypeLAST] = {NULL, NULL, NULL};
51 
getInstance(ClipBoardTypes type)52 PatternEditor::ClipBoard* PatternEditor::ClipBoard::getInstance(ClipBoardTypes type)
53 {
54 	if (instances[type] == NULL)
55 		instances[type] = new ClipBoard();
56 
57 	return instances[type];
58 }
59 
prepareUndo()60 void PatternEditor::prepareUndo()
61 {
62 	PatternEditorTools patternEditorTools(pattern);
63 	patternEditorTools.normalize();
64 
65 	undoUserData.clear();
66 	notifyListener(NotificationFeedUndoData);
67 
68 	delete before;
69 	before = new PatternUndoStackEntry(*pattern, cursor.channel, cursor.row, cursor.inner, &undoUserData);
70 }
71 
finishUndo(LastChanges lastChange,bool nonRepeat)72 bool PatternEditor::finishUndo(LastChanges lastChange, bool nonRepeat/* = false*/)
73 {
74 	bool result = false;
75 
76 	PatternEditorTools patternEditorTools(pattern);
77 	patternEditorTools.normalize();
78 
79 	undoUserData.clear();
80 	notifyListener(NotificationFeedUndoData);
81 
82 	PatternUndoStackEntry after(*pattern, cursor.channel, cursor.row, cursor.inner, &undoUserData);
83 	if (*before != after)
84 	{
85 		PatternEditorTools::Position afterPos;
86 		afterPos.channel = after.getCursorPositionChannel();
87 		afterPos.row = after.getCursorPositionRow();
88 		afterPos.inner = after.getCursorPositionInner();
89 
90 		PatternEditorTools::Position beforePos;
91 		beforePos.channel = before->getCursorPositionChannel();
92 		beforePos.row = before->getCursorPositionRow();
93 		beforePos.inner = before->getCursorPositionInner();
94 
95 		result = true;
96 
97 		lastOperationDidChangeRows = after.GetPattern().rows != before->GetPattern().rows;
98 		lastOperationDidChangeCursor = beforePos != afterPos;
99 		notifyListener(NotificationChanges);
100 		if (undoStack)
101 		{
102 			if (nonRepeat && this->lastChange != lastChange)
103 				undoStack->Push(*before);
104 			else if (!nonRepeat)
105 				undoStack->Push(*before);
106 			undoStack->Push(after);
107 			undoStack->Pop();
108 		}
109 	}
110 	this->lastChange = lastChange;
111 
112 	return result;
113 }
114 
PatternEditor()115 PatternEditor::PatternEditor() :
116 	EditorBase(),
117 	pattern(NULL),
118 	numVisibleChannels(-1),
119 	autoResize(false),
120 	currentInstrument(1),
121 	instrumentEnabled(true),
122 	instrumentBackTrace(false),
123 	currentOctave(5),
124 	before(NULL),
125 	undoStack(NULL),
126 	lastChange(LastChangeNone)
127 {
128 	// Undo history
129 	undoHistory = new UndoHistory<TXMPattern, PatternUndoStackEntry>(UNDOHISTORYSIZE_PATTERNEDITOR);
130 
131 	resetCursor();
132 	resetSelection();
133 
134 	memset(effectMacros, 0, sizeof(effectMacros));
135 }
136 
~PatternEditor()137 PatternEditor::~PatternEditor()
138 {
139 	delete undoHistory;
140 	delete undoStack;
141 	delete before;
142 }
143 
attachPattern(TXMPattern * pattern,XModule * module)144 void PatternEditor::attachPattern(TXMPattern* pattern, XModule* module)
145 {
146 	if (this->pattern == pattern && this->module == module)
147 		return;
148 
149 	// --------- update undo history information --------------------
150 	if (undoStack)
151 	{
152 		// if the undo stack is empty, we don't need to save current undo stack
153 		if (!undoStack->IsEmpty() || !undoStack->IsTop())
154 		{
155 			undoStack = undoHistory->getUndoStack(pattern, this->pattern, undoStack);
156 		}
157 		// delete it if it's empty
158 		else
159 		{
160 			delete undoStack;
161 			undoStack = NULL;
162 
163 			undoStack = undoHistory->getUndoStack(pattern, NULL, NULL);
164 		}
165 	}
166 
167 	attachModule(module);
168 	this->pattern = pattern;
169 
170 	// couldn't get any from history, create new one
171 	if (!undoStack)
172 	{
173 		undoStack = new PPUndoStack<PatternUndoStackEntry>(UNDODEPTH_PATTERNEDITOR);
174 	}
175 
176 	notifyListener(NotificationReload);
177 }
178 
reset()179 void PatternEditor::reset()
180 {
181 	resetSelection();
182 
183 	delete undoHistory;
184 	undoHistory = new UndoHistory<TXMPattern, PatternUndoStackEntry>(UNDOHISTORYSIZE_PATTERNEDITOR);
185 
186 	delete undoStack;
187 	undoStack = new PPUndoStack<PatternUndoStackEntry>(UNDODEPTH_PATTERNEDITOR);
188 }
189 
getNumChannels() const190 pp_int32 PatternEditor::getNumChannels() const
191 {
192 	if (numVisibleChannels >= 0)
193 		return numVisibleChannels;
194 
195 	if (pattern == NULL)
196 		return 0;
197 
198 	return pattern->channum;
199 }
200 
getNumRows() const201 pp_int32 PatternEditor::getNumRows() const
202 {
203 	if (pattern == NULL)
204 		return 0;
205 
206 	return pattern->rows;
207 }
208 
hasValidSelection()209 bool PatternEditor::hasValidSelection()
210 {
211 	return PatternEditorTools::hasValidSelection(pattern, selection.start, selection.end, getNumChannels());
212 }
213 
selectionContains(const PatternEditorTools::Position & pos)214 bool PatternEditor::selectionContains(const PatternEditorTools::Position& pos)
215 {
216 	return PatternEditorTools::selectionContains(pattern, selection.start, selection.end, pos);
217 }
218 
canMoveSelection(pp_int32 channels,pp_int32 rows)219 bool PatternEditor::canMoveSelection(pp_int32 channels, pp_int32 rows)
220 {
221 	PatternEditorTools::Position ss = selection.start;
222 	PatternEditorTools::Position se = selection.end;
223 	ss.channel += channels;
224 	ss.row += rows;
225 	se.channel += channels;
226 	se.row += rows;
227 	return PatternEditorTools::hasValidSelection(pattern, ss, se, getNumChannels());
228 }
229 
selectChannel(pp_int32 channel)230 void PatternEditor::selectChannel(pp_int32 channel)
231 {
232 	if (pattern == NULL)
233 		return;
234 
235 	selection.start.channel = channel;
236 	selection.start.row = 0;
237 	selection.start.inner = 0;
238 	selection.end.channel = channel;
239 	selection.end.row = pattern->rows-1;
240 	selection.end.inner = 7;
241 }
242 
selectAll()243 void PatternEditor::selectAll()
244 {
245 	if (pattern == NULL)
246 		return;
247 
248 	selection.start.channel = 0;
249 	selection.start.row = 0;
250 	selection.start.inner = 0;
251 	selection.end.channel = getNumChannels()-1;
252 	selection.end.row = pattern->rows-1;
253 	selection.end.inner = 7;
254 }
255 
getCurrentActiveInstrument()256 pp_int32 PatternEditor::getCurrentActiveInstrument()
257 {
258 	PatternTools patternTools;
259 
260 	if (pattern == NULL)
261 		return -1;
262 
263 	if (pattern->patternData == NULL)
264 		return -1;
265 
266 	if (!instrumentEnabled)
267 		return 0;
268 
269 	if (instrumentBackTrace)
270 	{
271 		for (pp_int32 i = cursor.row; i >= 0; i--)
272 		{
273 			patternTools.setPosition(pattern, cursor.channel, i);
274 
275 			pp_int32 ins = patternTools.getInstrument();
276 
277 			if (ins != 0)
278 			{
279 				return ins;
280 			}
281 		}
282 
283 		//return -1;
284 	}
285 
286 	return currentInstrument;
287 }
288 
undo()289 bool PatternEditor::undo()
290 {
291 	if (undoStack->IsEmpty()) return false;
292 	if (undoStack)
293 		return revoke(undoStack->Pop());
294 	return false;
295 }
296 
redo()297 bool PatternEditor::redo()
298 {
299 	if (undoStack->IsTop()) return false;
300 	if (undoStack)
301 		return revoke(undoStack->Advance());
302 	return false;
303 }
304 
revoke(const PatternUndoStackEntry * stackEntry)305 bool PatternEditor::revoke(const PatternUndoStackEntry* stackEntry)
306 {
307 	const TXMPattern& stackPattern = stackEntry->GetPattern();
308 
309 	enterCriticalSection();
310 
311 	bool res = false;
312 
313 	if (stackPattern.rows != pattern->rows ||
314 		stackPattern.channum != pattern->channum ||
315 		stackPattern.effnum != pattern->effnum)
316 	{
317 		pattern->rows = stackPattern.rows;
318 		pattern->channum = stackPattern.channum;
319 		pattern->effnum = stackPattern.effnum;
320 
321 		mp_sint32 patternSize = pattern->rows*pattern->channum*(2+pattern->effnum*2);
322 
323 		if (pattern->patternData)
324 		{
325 			delete[] pattern->patternData;
326 			pattern->patternData = new mp_ubyte[patternSize];
327 			memset(pattern->patternData, 0, patternSize);
328 		}
329 	}
330 
331 	if (stackPattern.rows == pattern->rows &&
332 		stackPattern.channum == pattern->channum &&
333 		stackPattern.effnum == pattern->effnum)
334 	{
335 		cursor.channel = stackEntry->getCursorPositionChannel();
336 		cursor.row = stackEntry->getCursorPositionRow();
337 		cursor.inner = stackEntry->getCursorPositionInner();
338 
339 		pattern->decompress(stackPattern.patternData, stackPattern.len);
340 
341 		// keep over userdata
342 		undoUserData = stackEntry->getUserData();
343 		notifyListener(NotificationFetchUndoData);
344 
345 		notifyListener(NotificationChanges);
346 		res = true;
347 	}
348 
349 	leaveCriticalSection();
350 
351 	return res;
352 }
353 
clearRange(const PatternEditorTools::Position & rangeStart,const PatternEditorTools::Position & rangeEnd)354 void PatternEditor::clearRange(const PatternEditorTools::Position& rangeStart, const PatternEditorTools::Position& rangeEnd)
355 {
356 	PatternEditorTools patternEditorTools(pattern);
357 	patternEditorTools.clearSelection(rangeStart, rangeEnd);
358 }
359 
clearSelection()360 void PatternEditor::clearSelection()
361 {
362 	if (pattern == NULL)
363 		return;
364 
365 	if (pattern->patternData == NULL)
366 		return;
367 
368 	prepareUndo();
369 
370 	clearRange(selection.start, selection.end);
371 
372 	finishUndo(LastChangeDeleteSelection);
373 }
374 
clearPattern()375 void PatternEditor::clearPattern()
376 {
377 	if (pattern == NULL)
378 		return;
379 
380 	if (pattern->patternData == NULL)
381 		return;
382 
383 	pp_int32 patSize = pattern->channum * (pattern->effnum*2+2) * pattern->rows;
384 
385 	memset(pattern->patternData, 0, patSize);
386 }
387 
cut(ClipBoard & clipBoard)388 void PatternEditor::cut(ClipBoard& clipBoard)
389 {
390 	if (pattern == NULL)
391 		return;
392 
393 	if (pattern->patternData == NULL)
394 		return;
395 
396 	prepareUndo();
397 
398 	clipBoard.makeCopy(*pattern,
399 					   getSelection().start,
400 					   getSelection().end,
401 					   true);
402 
403 	finishUndo(LastChangeCut);
404 }
405 
copy(ClipBoard & clipBoard)406 void PatternEditor::copy(ClipBoard& clipBoard)
407 {
408 	clipBoard.makeCopy(*pattern,
409 					   getSelection().start,
410 					   getSelection().end);
411 }
412 
paste(ClipBoard & clipBoard,bool transparent,pp_int32 fromChannel)413 void PatternEditor::paste(ClipBoard& clipBoard, bool transparent/* = false*/, pp_int32 fromChannel/* = -1*/)
414 {
415 	if (pattern == NULL)
416 		return;
417 
418 	if (pattern->patternData == NULL)
419 		return;
420 
421 	prepareUndo();
422 
423 	if (autoResize && cursor.row + clipBoard.getNumRows() > pattern->rows)
424 	{
425 		pp_int32 newLen = cursor.row + clipBoard.getNumRows();
426 		if (newLen > 256)
427 			newLen = 256;
428 		resizePattern(newLen, false);
429 	}
430 
431 	if (fromChannel == -1)
432 		clipBoard.paste(*pattern, cursor.channel, cursor.row, transparent);
433 	else
434 		clipBoard.paste(*pattern, fromChannel, cursor.row, transparent);
435 
436 	finishUndo(LastChangePaste);
437 }
438 
cut(ClipBoardTypes clipBoardType)439 void PatternEditor::cut(ClipBoardTypes clipBoardType)
440 {
441 	cut(*ClipBoard::getInstance(clipBoardType));
442 }
443 
copy(ClipBoardTypes clipBoardType)444 void PatternEditor::copy(ClipBoardTypes clipBoardType)
445 {
446 	copy(*ClipBoard::getInstance(clipBoardType));
447 }
448 
paste(ClipBoardTypes clipBoardType,bool transparent,pp_int32 fromChannel)449 void PatternEditor::paste(ClipBoardTypes clipBoardType, bool transparent/* = false*/, pp_int32 fromChannel/* = -1*/)
450 {
451 	paste(*ClipBoard::getInstance(clipBoardType), transparent, fromChannel);
452 }
453 
resizePattern(pp_int32 newRowNum,bool withUndo)454 bool PatternEditor::resizePattern(pp_int32 newRowNum, bool withUndo /*= true*/)
455 {
456 	if (newRowNum < 1 || newRowNum > 256)
457 		return false;
458 
459 	if (pattern == NULL)
460 		return false;
461 
462 	if (pattern->patternData == NULL)
463 		return false;
464 
465 	if (newRowNum == pattern->rows)
466 		return true;
467 
468 	enterCriticalSection();
469 
470 	mp_sint32 slotSize = pattern->effnum * 2 + 2;
471 	// allocate half of the space of the current pattern
472 	mp_sint32 patternSize = slotSize * pattern->channum * newRowNum;
473 
474 	mp_ubyte* newPatternData = new mp_ubyte[patternSize];
475 
476 	memset(newPatternData, 0, patternSize);
477 
478 	mp_sint32 numRows = newRowNum < pattern->rows ? newRowNum : pattern->rows;
479 
480 	for (mp_sint32 i = 0; i < numRows; i++)
481 	{
482 		mp_sint32 srcOffset = i * slotSize * pattern->channum;
483 		mp_sint32 dstOffset = i * slotSize * pattern->channum;
484 		for (mp_sint32 j = 0; j < slotSize * pattern->channum; j++)
485 			newPatternData[dstOffset++] = pattern->patternData[srcOffset++];
486 	}
487 
488 	if (withUndo)
489 	{
490 		prepareUndo();
491 
492 		delete[] pattern->patternData;
493 
494 		pattern->patternData = newPatternData;
495 
496 		pattern->rows = newRowNum;
497 
498 		// see if something has changed, if this is the case
499 		// save original & changes
500 		// Special treatment for pattern resizing:
501 		// If user resizes pattern and the last stack entry has already been
502 		// a resize modification we don't store the current changes
503 		finishUndo(LastChangeResizePattern, true);
504 	}
505 	else
506 	{
507 		delete[] pattern->patternData;
508 
509 		pattern->patternData = newPatternData;
510 
511 		pattern->rows = newRowNum;
512 
513 		lastOperationDidChangeRows = true;
514 		lastOperationDidChangeCursor = false;
515 		notifyListener(NotificationChanges);
516 	}
517 
518 	leaveCriticalSection();
519 
520 	return true;
521 }
522 
expandPattern()523 bool PatternEditor::expandPattern()
524 {
525 	if (pattern == NULL)
526 		return false;
527 
528 	if (pattern->patternData == NULL)
529 		return false;
530 
531 	enterCriticalSection();
532 
533 	prepareUndo();
534 
535 	PatternEditorTools patternEditorTools(pattern);
536 	bool res = patternEditorTools.expandPattern();
537 
538 	// see if something has changed, if this is the case
539 	// save original & changes
540 	finishUndo(LastChangeExpandPattern);
541 
542 	leaveCriticalSection();
543 
544 	return res;
545 }
546 
shrinkPattern()547 bool PatternEditor::shrinkPattern()
548 {
549 	if (pattern == NULL)
550 		return false;
551 
552 	if (pattern->patternData == NULL)
553 		return false;
554 
555 	enterCriticalSection();
556 
557 	prepareUndo();
558 
559 	PatternEditorTools patternEditorTools(pattern);
560 	bool res = patternEditorTools.shrinkPattern();
561 
562 	// see if something has changed, if this is the case
563 	// save original & changes
564 	finishUndo(LastChangeShrinkPattern);
565 
566 	leaveCriticalSection();
567 
568 	return res;
569 }
570 
loadExtendedPattern(const PPSystemString & fileName)571 bool PatternEditor::loadExtendedPattern(const PPSystemString& fileName)
572 {
573 	if (pattern == NULL)
574 		return false;
575 
576 	if (pattern->patternData == NULL)
577 		return false;
578 
579 	enterCriticalSection();
580 
581 	prepareUndo();
582 
583 	bool res = pattern->loadExtendedPattern(fileName);
584 
585 	// see if something has changed, if this is the case
586 	// save original & changes
587 	finishUndo(LastChangeLoadXPattern);
588 
589 	leaveCriticalSection();
590 
591 	return res;
592 }
593 
saveExtendedPattern(const PPSystemString & fileName)594 bool PatternEditor::saveExtendedPattern(const PPSystemString& fileName)
595 {
596 	if (pattern == NULL)
597 		return false;
598 
599 	if (pattern->patternData == NULL)
600 		return false;
601 
602 	return pattern->saveExtendedPattern(fileName);
603 }
604 
loadExtendedTrack(const PPSystemString & fileName)605 bool PatternEditor::loadExtendedTrack(const PPSystemString& fileName)
606 {
607 	if (pattern == NULL)
608 		return 0;
609 
610 	if (pattern->patternData == NULL)
611 		return 0;
612 
613 	enterCriticalSection();
614 
615 	prepareUndo();
616 
617 	bool res = pattern->loadExtendedTrack(fileName, cursor.channel);
618 
619 	// see if something has changed, if this is the case
620 	// save original & changes
621 	finishUndo(LastChangeLoadXTrack);
622 
623 	leaveCriticalSection();
624 
625 	return res;
626 }
627 
saveExtendedTrack(const PPSystemString & fileName)628 bool PatternEditor::saveExtendedTrack(const PPSystemString& fileName)
629 {
630 	if (pattern == NULL)
631 		return false;
632 
633 	if (pattern->patternData == NULL)
634 		return false;
635 
636 	return pattern->saveExtendedTrack(fileName, cursor.channel);
637 }
638 
insRemapTrack(pp_int32 oldIns,pp_int32 newIns)639 pp_int32 PatternEditor::insRemapTrack(pp_int32 oldIns, pp_int32 newIns)
640 {
641 	if (pattern == NULL)
642 		return 0;
643 
644 	if (pattern->patternData == NULL)
645 		return 0;
646 
647 	prepareUndo();
648 
649 	PatternEditorTools patternEditorTools(pattern);
650 
651 	pp_int32 resCnt = patternEditorTools.insRemapTrack(cursor.channel, oldIns, newIns);
652 
653 	finishUndo(LastChangeInsRemapTrack);
654 
655 	return resCnt;
656 }
657 
insRemapPattern(pp_int32 oldIns,pp_int32 newIns)658 pp_int32 PatternEditor::insRemapPattern(pp_int32 oldIns, pp_int32 newIns)
659 {
660 	if (pattern == NULL)
661 		return 0;
662 
663 	if (pattern->patternData == NULL)
664 		return 0;
665 
666 	prepareUndo();
667 
668 	PatternEditorTools patternEditorTools(pattern);
669 
670 	pp_int32 resCnt = patternEditorTools.insRemap(oldIns, newIns);
671 
672 	finishUndo(LastChangeInsRemapPattern);
673 
674 	return resCnt;
675 }
676 
insIncSelection()677 pp_int32 PatternEditor::insIncSelection()
678 {
679 	if (pattern == NULL)
680 		return 0;
681 
682 	if (pattern->patternData == NULL)
683 		return 0;
684 
685 	prepareUndo();
686 
687 	PatternEditorTools patternEditorTools(pattern);
688 	pp_int32 resCnt = patternEditorTools.insIncSelection(getSelection().start, getSelection().end);
689 
690 	finishUndo(LastChangeInsIncSelection);
691 
692 	return resCnt;
693 }
694 
insDecSelection()695 pp_int32 PatternEditor::insDecSelection()
696 {
697 	if (pattern == NULL)
698 		return 0;
699 
700 	if (pattern->patternData == NULL)
701 		return 0;
702 
703 	prepareUndo();
704 
705 	PatternEditorTools patternEditorTools(pattern);
706 	pp_int32 resCnt = patternEditorTools.insDecSelection(getSelection().start, getSelection().end);
707 
708 	finishUndo(LastChangeInsDecSelection);
709 
710 	return resCnt;
711 }
712 
insIncTrack()713 pp_int32 PatternEditor::insIncTrack()
714 {
715 	if (pattern == NULL)
716 		return 0;
717 
718 	if (pattern->patternData == NULL)
719 		return 0;
720 
721 	prepareUndo();
722 
723 	PatternEditorTools patternEditorTools(pattern);
724 	pp_int32 resCnt = patternEditorTools.insIncTrack(getCursor().channel);
725 
726 	finishUndo(LastChangeInsIncTrack);
727 
728 	return resCnt;
729 }
730 
insDecTrack()731 pp_int32 PatternEditor::insDecTrack()
732 {
733 	if (pattern == NULL)
734 		return 0;
735 
736 	if (pattern->patternData == NULL)
737 		return 0;
738 
739 	prepareUndo();
740 
741 	PatternEditorTools patternEditorTools(pattern);
742 	pp_int32 resCnt = patternEditorTools.insDecTrack(getCursor().channel);
743 
744 	finishUndo(LastChangeInsDecTrack);
745 
746 	return resCnt;
747 }
748 
insRemapSelection(pp_int32 oldIns,pp_int32 newIns)749 pp_int32 PatternEditor::insRemapSelection(pp_int32 oldIns, pp_int32 newIns)
750 {
751 	if (pattern == NULL)
752 		return 0;
753 
754 	if (pattern->patternData == NULL)
755 		return 0;
756 
757 	prepareUndo();
758 
759 	PatternEditorTools patternEditorTools(pattern);
760 	pp_int32 resCnt = patternEditorTools.insRemapSelection(getSelection().start, getSelection().end, oldIns, newIns);
761 
762 	finishUndo(LastChangeInsRemapSelection);
763 
764 	return resCnt;
765 }
766 
noteTransposeTrackCore(const PatternEditorTools::TransposeParameters & transposeParameters,bool evaluate)767 pp_int32 PatternEditor::noteTransposeTrackCore(const PatternEditorTools::TransposeParameters& transposeParameters, bool evaluate)
768 {
769 	if (pattern == NULL)
770 		return 0;
771 
772 	if (pattern->patternData == NULL)
773 		return 0;
774 
775 	prepareUndo();
776 
777 	pp_int32 resCnt = 0;
778 	pp_int32 fuckupCnt = 0;
779 
780 	PatternEditorTools patternEditorTools(pattern);
781 
782 	if (!evaluate)
783 		resCnt = patternEditorTools.noteTransposeTrack(cursor.channel, transposeParameters, evaluate);
784 	else
785 		fuckupCnt = patternEditorTools.noteTransposeTrack(cursor.channel, transposeParameters, evaluate);
786 
787 	finishUndo(LastChangeNoteTransposeTrack);
788 
789 	if (!evaluate)
790 		return resCnt;
791 	else
792 		return fuckupCnt;
793 }
794 
noteTransposePatternCore(const PatternEditorTools::TransposeParameters & transposeParameters,bool evaluate)795 pp_int32 PatternEditor::noteTransposePatternCore(const PatternEditorTools::TransposeParameters& transposeParameters, bool evaluate)
796 {
797 	if (pattern == NULL)
798 		return 0;
799 
800 	if (pattern->patternData == NULL)
801 		return 0;
802 
803 	prepareUndo();
804 
805 	PatternEditorTools patternEditorTools(pattern);
806 
807 	pp_int32 resCnt = 0;
808 	pp_int32 fuckupCnt = 0;
809 
810 	if (!evaluate)
811 		resCnt = patternEditorTools.noteTranspose(transposeParameters, evaluate);
812 	else
813 		fuckupCnt = patternEditorTools.noteTranspose(transposeParameters, evaluate);
814 
815 	finishUndo(LastChangeNoteTransposePattern);
816 
817 	if (!evaluate)
818 		return resCnt;
819 	else
820 		return fuckupCnt;
821 }
822 
noteTransposeSelectionCore(const PatternEditorTools::TransposeParameters & transposeParameters,bool evaluate)823 pp_int32 PatternEditor::noteTransposeSelectionCore(const PatternEditorTools::TransposeParameters& transposeParameters, bool evaluate)
824 {
825 	if (pattern == NULL)
826 		return 0;
827 
828 	if (pattern->patternData == NULL)
829 		return 0;
830 
831 	pp_int32 resCnt = 0;
832 	pp_int32 fuckupCnt = 0;
833 
834 	prepareUndo();
835 
836 	PatternEditorTools patternEditorTools(pattern);
837 
838 	if (!evaluate)
839 		resCnt = patternEditorTools.noteTransposeSelection(getSelection().start, getSelection().end, transposeParameters, evaluate);
840 	else
841 		fuckupCnt = patternEditorTools.noteTransposeSelection(getSelection().start, getSelection().end, transposeParameters, evaluate);
842 
843 	finishUndo(LastChangeNoteTransposeSelection);
844 
845 	if (!evaluate)
846 		return resCnt;
847 	else
848 		return fuckupCnt;
849 }
850 
interpolateValuesInSelection()851 pp_int32 PatternEditor::interpolateValuesInSelection()
852 {
853 	if (pattern == NULL)
854 		return 0;
855 
856 	if (pattern->patternData == NULL)
857 		return 0;
858 
859 	pp_int32 stats = 0;
860 
861 	prepareUndo();
862 
863 	PatternEditorTools patternEditorTools(pattern);
864 	stats = patternEditorTools.interpolateValuesInSelection(getSelection().start, getSelection().end);
865 
866 	finishUndo(LastChangeNoteInterpolate);
867 
868 	return stats;
869 }
870 
splitTrack(pp_int32 useChannels,bool selectionOnly,bool insertNoteOff)871 pp_int32 PatternEditor::splitTrack(pp_int32 useChannels, bool selectionOnly, bool insertNoteOff)
872 {
873 	if (pattern == NULL)
874 		return 0;
875 
876 	if (pattern->patternData == NULL)
877 		return 0;
878 
879 	pp_int32 stats = 0;
880 
881 	pp_int32 chn = cursor.channel;
882 
883 	pp_int32 fromRow = 0;
884 	pp_int32 toRow = pattern->rows;
885 
886 	if (selectionOnly && hasValidSelection())
887 	{
888 		if (getSelection().start.channel != getSelection().end.channel)
889 			return -1;
890 
891 		chn = getSelection().start.channel;
892 		fromRow = getSelection().start.row;
893 		toRow = getSelection().end.row+1;
894 	}
895 	else if (selectionOnly && !hasValidSelection())
896 		return 0;
897 
898 	prepareUndo();
899 
900 	PatternEditorTools patternEditorTools(pattern);
901 
902 	stats = patternEditorTools.splitTrack(useChannels, insertNoteOff, chn, fromRow, toRow, getNumChannels());
903 
904 	finishUndo(LastChangeSplitTrack);
905 
906 	return stats;
907 }
908 
swapChannels(pp_int32 dstChannel,pp_int32 srcChannel)909 pp_int32 PatternEditor::swapChannels(pp_int32 dstChannel, pp_int32 srcChannel)
910 {
911 	if (pattern == NULL)
912 		return 0;
913 
914 	if (pattern->patternData == NULL)
915 		return 0;
916 
917 	if (dstChannel == srcChannel)
918 		return 0;
919 
920 	prepareUndo();
921 
922 	PatternEditorTools patternEditorTools(pattern);
923 
924 	pp_int32 stats = patternEditorTools.swapChannels(dstChannel, srcChannel);
925 
926 	finishUndo(LastChangeSwapChannels);
927 
928 	return stats;
929 }
930 
scaleVolume(const PatternEditorTools::Position & startSelection,const PatternEditorTools::Position & endSelection,float startScale,float endScale)931 pp_int32 PatternEditor::scaleVolume(const PatternEditorTools::Position& startSelection, const PatternEditorTools::Position& endSelection, float startScale, float endScale)
932 {
933 	prepareUndo();
934 
935 	PatternEditorTools patternEditorTools(pattern);
936 
937 	pp_int32 stats = patternEditorTools.scaleVolume(startSelection,
938 											   endSelection,
939 											   startScale,
940 											   endScale,
941 											   getNumChannels(), module);
942 
943 	finishUndo(LastChangeScaleVolume);
944 
945 	return stats;
946 }
947 
scaleVolumeTrack(float startScale,float endScale)948 pp_int32 PatternEditor::scaleVolumeTrack(float startScale, float endScale)
949 {
950 	if (pattern == NULL)
951 		return 0;
952 
953 	PatternEditorTools::Position startSel, endSel;
954 
955 	startSel.row = startSel.inner = 0;
956 	startSel.channel = cursor.channel;
957 	endSel = startSel;
958 	endSel.row = pattern->rows-1;
959 
960 	return scaleVolume(startSel, endSel, startScale, endScale);
961 }
962 
scaleVolumePattern(float startScale,float endScale)963 pp_int32 PatternEditor::scaleVolumePattern(float startScale, float endScale)
964 {
965 	if (pattern == NULL)
966 		return 0;
967 
968 	PatternEditorTools::Position startSel, endSel;
969 
970 	startSel.channel = startSel.row = startSel.inner = 0;
971 	endSel.channel = pattern->channum-1; endSel.row = pattern->rows-1; endSel.inner = 7;
972 
973 	return scaleVolume(startSel, endSel, startScale, endScale);
974 }
975 
scaleVolumeSelection(float startScale,float endScale)976 pp_int32 PatternEditor::scaleVolumeSelection(float startScale, float endScale)
977 {
978 	if (pattern == NULL || !hasValidSelection())
979 		return 0;
980 
981 	return scaleVolume(getSelection().start, getSelection().end, startScale, endScale);
982 }
983 
zeroOperandsTrack(const PatternEditorTools::OperandOptimizeParameters & optimizeParameters,bool evaluate)984 pp_int32 PatternEditor::zeroOperandsTrack(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate)
985 {
986 	if (pattern == NULL)
987 		return 0;
988 
989 	if (pattern->patternData == NULL)
990 		return 0;
991 
992 	prepareUndo();
993 
994 	pp_int32 resCnt = 0;
995 
996 	PatternEditorTools patternEditorTools(pattern);
997 
998 	resCnt = patternEditorTools.zeroOperandsTrack(cursor.channel, optimizeParameters, evaluate);
999 
1000 	finishUndo(LastChangeZeroOperandsTrack);
1001 
1002 	return resCnt;
1003 }
1004 
zeroOperandsPattern(const PatternEditorTools::OperandOptimizeParameters & optimizeParameters,bool evaluate)1005 pp_int32 PatternEditor::zeroOperandsPattern(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate)
1006 {
1007 	if (pattern == NULL)
1008 		return 0;
1009 
1010 	if (pattern->patternData == NULL)
1011 		return 0;
1012 
1013 	prepareUndo();
1014 
1015 	pp_int32 resCnt = 0;
1016 
1017 	PatternEditorTools patternEditorTools(pattern);
1018 
1019 	resCnt = patternEditorTools.zeroOperands(optimizeParameters, evaluate);
1020 
1021 	finishUndo(LastChangeZeroOperandsPattern);
1022 
1023 	return resCnt;
1024 }
1025 
zeroOperandsSelection(const PatternEditorTools::OperandOptimizeParameters & optimizeParameters,bool evaluate)1026 pp_int32 PatternEditor::zeroOperandsSelection(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate)
1027 {
1028 	if (pattern == NULL)
1029 		return 0;
1030 
1031 	if (pattern->patternData == NULL)
1032 		return 0;
1033 
1034 	prepareUndo();
1035 
1036 	pp_int32 resCnt = 0;
1037 
1038 	PatternEditorTools patternEditorTools(pattern);
1039 
1040 	resCnt = patternEditorTools.zeroOperandsSelection(getSelection().start, getSelection().end, optimizeParameters, evaluate);
1041 
1042 	finishUndo(LastChangeZeroOperandsSelection);
1043 
1044 	return resCnt;
1045 }
1046 
fillOperandsTrack(const PatternEditorTools::OperandOptimizeParameters & optimizeParameters,bool evaluate)1047 pp_int32 PatternEditor::fillOperandsTrack(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate)
1048 {
1049 	if (pattern == NULL)
1050 		return 0;
1051 
1052 	if (pattern->patternData == NULL)
1053 		return 0;
1054 
1055 	prepareUndo();
1056 
1057 	pp_int32 resCnt = 0;
1058 
1059 	PatternEditorTools patternEditorTools(pattern);
1060 
1061 	resCnt = patternEditorTools.fillOperandsTrack(cursor.channel, optimizeParameters, evaluate);
1062 
1063 	finishUndo(LastChangeFillOperandsTrack);
1064 
1065 	return resCnt;
1066 }
1067 
fillOperandsPattern(const PatternEditorTools::OperandOptimizeParameters & optimizeParameters,bool evaluate)1068 pp_int32 PatternEditor::fillOperandsPattern(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate)
1069 {
1070 	if (pattern == NULL)
1071 		return 0;
1072 
1073 	if (pattern->patternData == NULL)
1074 		return 0;
1075 
1076 	prepareUndo();
1077 
1078 	pp_int32 resCnt = 0;
1079 
1080 	PatternEditorTools patternEditorTools(pattern);
1081 
1082 	resCnt = patternEditorTools.fillOperands(optimizeParameters, evaluate);
1083 
1084 	finishUndo(LastChangeFillOperandsPattern);
1085 
1086 	return resCnt;
1087 }
1088 
fillOperandsSelection(const PatternEditorTools::OperandOptimizeParameters & optimizeParameters,bool evaluate)1089 pp_int32 PatternEditor::fillOperandsSelection(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate)
1090 {
1091 	if (pattern == NULL)
1092 		return 0;
1093 
1094 	if (pattern->patternData == NULL)
1095 		return 0;
1096 
1097 	prepareUndo();
1098 
1099 	pp_int32 resCnt = 0;
1100 
1101 	PatternEditorTools patternEditorTools(pattern);
1102 
1103 	resCnt = patternEditorTools.fillOperandsSelection(getSelection().start, getSelection().end, optimizeParameters, evaluate);
1104 
1105 	finishUndo(LastChangeFillOperandsSelection);
1106 
1107 	return resCnt;
1108 }
1109 
relocateCommandsTrack(const PatternEditorTools::RelocateParameters & relocateParameters,bool evaluate)1110 pp_int32 PatternEditor::relocateCommandsTrack(const PatternEditorTools::RelocateParameters& relocateParameters, bool evaluate)
1111 {
1112 	if (pattern == NULL)
1113 		return 0;
1114 
1115 	if (pattern->patternData == NULL)
1116 		return 0;
1117 
1118 	prepareUndo();
1119 
1120 	pp_int32 resCnt = 0;
1121 
1122 	PatternEditorTools patternEditorTools(pattern);
1123 
1124 	resCnt = patternEditorTools.relocateCommandsTrack(cursor.channel, relocateParameters, evaluate);
1125 
1126 	finishUndo(LastChangeRelocateCommandsTrack);
1127 
1128 	return resCnt;
1129 }
1130 
relocateCommandsPattern(const PatternEditorTools::RelocateParameters & relocateParameters,bool evaluate)1131 pp_int32 PatternEditor::relocateCommandsPattern(const PatternEditorTools::RelocateParameters& relocateParameters, bool evaluate)
1132 {
1133 	if (pattern == NULL)
1134 		return 0;
1135 
1136 	if (pattern->patternData == NULL)
1137 		return 0;
1138 
1139 	prepareUndo();
1140 
1141 	pp_int32 resCnt = 0;
1142 
1143 	PatternEditorTools patternEditorTools(pattern);
1144 
1145 	resCnt = patternEditorTools.relocateCommands(relocateParameters, evaluate);
1146 
1147 	finishUndo(LastChangeRelocateCommandsPattern);
1148 
1149 	return resCnt;
1150 }
1151 
relocateCommandsSelection(const PatternEditorTools::RelocateParameters & relocateParameters,bool evaluate)1152 pp_int32 PatternEditor::relocateCommandsSelection(const PatternEditorTools::RelocateParameters& relocateParameters, bool evaluate)
1153 {
1154 	if (pattern == NULL)
1155 		return 0;
1156 
1157 	if (pattern->patternData == NULL)
1158 		return 0;
1159 
1160 	prepareUndo();
1161 
1162 	pp_int32 resCnt = 0;
1163 
1164 	PatternEditorTools patternEditorTools(pattern);
1165 
1166 	resCnt = patternEditorTools.relocateCommandsSelection(getSelection().start, getSelection().end, relocateParameters, evaluate);
1167 
1168 	finishUndo(LastChangeRelocateCommandsSelection);
1169 
1170 	return resCnt;
1171 }
1172 
writeNote(pp_int32 note,bool withUndo,PatternAdvanceInterface * advanceImpl)1173 bool PatternEditor::writeNote(pp_int32 note,
1174 							  bool withUndo/* = false*/,
1175 							  PatternAdvanceInterface* advanceImpl/* = NULL*/)
1176 {
1177 	if (withUndo)
1178 		prepareUndo();
1179 
1180 	PatternTools patternTools;
1181 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1182 
1183 	// means to delete note
1184 	if (note == 0xFF)
1185 	{
1186 		patternTools.setNote(0);
1187 
1188 		if (advanceImpl)
1189 			advanceImpl->advance();
1190 
1191 		if (withUndo)
1192 			finishUndo(LastChangeSlotChange);
1193 		return true;
1194 	}
1195 
1196 	if (note >= 1 && note <= PatternTools::getNoteOffNote())
1197 	{
1198 		pp_int32 currentInstrument = getCurrentActiveInstrument();
1199 
1200 		patternTools.setNote(note);
1201 		if (currentInstrument && note != PatternTools::getNoteOffNote())
1202 			patternTools.setInstrument(currentInstrument);
1203 
1204 		if (advanceImpl)
1205 			advanceImpl->advance();
1206 
1207 		if (withUndo)
1208 			finishUndo(LastChangeSlotChange);
1209 		return true;
1210 	}
1211 
1212 	return false;
1213 }
1214 
writeDirectNote(pp_int32 note,pp_int32 track,pp_int32 row,pp_int32 order)1215 void PatternEditor::writeDirectNote(pp_int32 note,
1216 									pp_int32 track/* = -1*/,
1217 									pp_int32 row/* = -1*/,
1218 									pp_int32 order/* = -1*/)
1219 {
1220 	TXMPattern* pattern = this->pattern;
1221 	if (order != -1)
1222 		pattern = &module->phead[module->header.ord[order]];
1223 
1224 	if (track == -1)
1225 		track = cursor.channel;
1226 
1227 	if (row == -1)
1228 		row = cursor.row;
1229 
1230 	PatternTools patternTools;
1231 	patternTools.setPosition(pattern, track, row);
1232 
1233 	// means to delete note
1234 	if (note == 0xFF)
1235 	{
1236 		patternTools.setNote(0);
1237 	}
1238 
1239 	if (note >= 1 && note <= PatternTools::getNoteOffNote())
1240 	{
1241 		pp_int32 currentInstrument = getCurrentActiveInstrument();
1242 
1243 		patternTools.setNote(note);
1244 		if (currentInstrument && note != PatternTools::getNoteOffNote())
1245 			patternTools.setInstrument(currentInstrument);
1246 	}
1247 }
1248 
writeEffect(pp_int32 effNum,pp_uint8 eff,pp_uint8 op,bool withUndo,PatternAdvanceInterface * advanceImpl)1249 bool PatternEditor::writeEffect(pp_int32 effNum, pp_uint8 eff, pp_uint8 op,
1250 								bool withUndo/* = false*/,
1251 								PatternAdvanceInterface* advanceImpl/* = NULL*/)
1252 {
1253 	if (withUndo)
1254 		prepareUndo();
1255 
1256 	PatternTools patternTools;
1257 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1258 
1259 	// only write effect, when valid effect
1260 	// (0 is not a valid effect in my internal format, arpeggio is mapped to 0x20)
1261 	if (eff)
1262 		patternTools.setEffect(effNum, eff, op);
1263 	else
1264 		return false;
1265 
1266 	if (advanceImpl)
1267 		advanceImpl->advance();
1268 
1269 	if (withUndo)
1270 		finishUndo(LastChangeSlotChange);
1271 	return true;
1272 }
1273 
writeDirectEffect(pp_int32 effNum,pp_uint8 eff,pp_uint8 op,pp_int32 track,pp_int32 row,pp_int32 order)1274 void PatternEditor::writeDirectEffect(pp_int32 effNum, pp_uint8 eff, pp_uint8 op,
1275 									  pp_int32 track/* = -1*/,
1276 									  pp_int32 row/* = -1*/,
1277 									  pp_int32 order/* = -1*/)
1278 {
1279 	TXMPattern* pattern = this->pattern;
1280 	if (order != -1)
1281 		pattern = &module->phead[module->header.ord[order]];
1282 
1283 	if (track == -1)
1284 		track = cursor.channel;
1285 
1286 	if (row == -1)
1287 		row = cursor.row;
1288 
1289 	PatternTools patternTools;
1290 	patternTools.setPosition(pattern, track, row);
1291 
1292 	// only write effect, when valid effect
1293 	// (0 is not a valid effect in my internal format, arpeggio is mapped to 0x20)
1294 	if (eff)
1295 		patternTools.setEffect(effNum, eff, op);
1296 }
1297 
1298 
writeInstrument(NibbleTypes nibleType,pp_uint8 value,bool withUndo,PatternAdvanceInterface * advanceImpl)1299 bool PatternEditor::writeInstrument(NibbleTypes nibleType, pp_uint8 value, bool withUndo/* = false*/, PatternAdvanceInterface* advanceImpl/* = NULL*/)
1300 {
1301 	if (withUndo)
1302 		prepareUndo();
1303 
1304 	PatternTools patternTools;
1305 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1306 
1307 	if (nibleType == NibbleTypeBoth)
1308 	{
1309 		patternTools.setInstrument(value);
1310 
1311 		if (advanceImpl)
1312 			advanceImpl->advance();
1313 
1314 		if (withUndo)
1315 			finishUndo(LastChangeSlotChange);
1316 		return true;
1317 	}
1318 
1319 	pp_uint32 i = patternTools.getInstrument();
1320 
1321 	if (nibleType == NibbleTypeHigh)
1322 	{
1323 		i &= 0x0F;
1324 		i |= (pp_uint32)value << 4;
1325 		patternTools.setInstrument(i);
1326 
1327 		if (advanceImpl)
1328 			advanceImpl->advance();
1329 
1330 		if (withUndo)
1331 			finishUndo(LastChangeSlotChange);
1332 		return true;
1333 	}
1334 	else if (nibleType == NibbleTypeLow)
1335 	{
1336 		i &= 0xF0;
1337 		i |= (pp_uint32)value;
1338 		patternTools.setInstrument(i);
1339 
1340 		if (advanceImpl)
1341 			advanceImpl->advance();
1342 
1343 		if (withUndo)
1344 			finishUndo(LastChangeSlotChange);
1345 		return true;
1346 	}
1347 
1348 	return false;
1349 }
1350 
writeFT2Volume(NibbleTypes nibleType,pp_uint8 value,bool withUndo,PatternAdvanceInterface * advanceImpl)1351 bool PatternEditor::writeFT2Volume(NibbleTypes nibleType, pp_uint8 value, bool withUndo/* = false*/, PatternAdvanceInterface* advanceImpl/* = NULL*/)
1352 {
1353 	if (withUndo)
1354 		prepareUndo();
1355 
1356 	PatternTools patternTools;
1357 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1358 
1359 	if (value == 0xFF)
1360 	{
1361 		patternTools.setFirstEffect(0, 0);
1362 
1363 		if (advanceImpl)
1364 			advanceImpl->advance();
1365 
1366 		if (withUndo)
1367 			finishUndo(LastChangeSlotChange);
1368 		return true;
1369 	}
1370 
1371 	pp_int32 eff,op;
1372 
1373 	patternTools.getFirstEffect(eff, op);
1374 	patternTools.convertEffectsToFT2(eff, op);
1375 
1376 	pp_int32 volume = patternTools.getVolumeFromEffect(eff, op) - 0x10;
1377 
1378 	if (volume < 0)
1379 		volume = 0;
1380 
1381 	if (volume >= 0xF0)
1382 		return false;
1383 
1384 	if (volume >= 0)
1385 	{
1386 		if (nibleType == NibbleTypeHigh)
1387 		{
1388 			volume &= 0x0F;
1389 			volume |= (pp_int32)value << 4;
1390 
1391 			if (volume>=0xF0)
1392 				return false;
1393 			else if ((volume > 0x40 && volume < 0x50))
1394 				volume = 0x40;
1395 
1396 			if (volume >= 0x50 && ((volume & 0xF) == 0))
1397 			{
1398 				switch ((volume+0x10) >> 4)
1399 				{
1400 					// For the following effects we don't allow zero operand
1401 					case 0x6: // Volslide down
1402 					case 0x7: // Volslide up
1403 					case 0x8: // Fine volslide down
1404 					case 0x9: // Fine volslide up
1405 					case 0xa: // Set vibrato speed
1406 					case 0xd: // Panslide left
1407 					case 0xe: // Panslide right
1408 						volume |= 1;
1409 						break;
1410 				}
1411 			}
1412 		}
1413 		else if (nibleType == NibbleTypeLow)
1414 		{
1415 			volume &= 0xF0;
1416 			volume |= (pp_int32)value;
1417 
1418 			if (volume>=0xF0)
1419 				return false;
1420 			else if ((volume > 0x40 && volume < 0x50))
1421 				volume = 0x40;
1422 
1423 			if (volume >= 0x50 && ((volume & 0xF) == 0))
1424 			{
1425 				switch ((volume+0x10) >> 4)
1426 				{
1427 					// For the following effects we don't allow zero operand
1428 					case 0x6: // Volslide down
1429 					case 0x7: // Volslide up
1430 					case 0x8: // Fine volslide down
1431 					case 0x9: // Fine volslide up
1432 					case 0xa: // Set vibrato speed
1433 					case 0xd: // Panslide left
1434 					case 0xe: // Panslide right
1435 						volume |= 1;
1436 						break;
1437 				}
1438 			}
1439 		}
1440 
1441 		patternTools.convertVolumeToEffect(volume + 0x10, eff, op);
1442 		patternTools.setFirstEffect(eff, op);
1443 
1444 		if (advanceImpl)
1445 			advanceImpl->advance();
1446 
1447 		if (withUndo)
1448 			finishUndo(LastChangeSlotChange);
1449 		return true;
1450 	}
1451 
1452 	return false;
1453 }
1454 
writeEffectNumber(pp_uint8 value,bool withUndo,PatternAdvanceInterface * advanceImpl)1455 bool PatternEditor::writeEffectNumber(pp_uint8 value, bool withUndo/* = false*/, PatternAdvanceInterface* advanceImpl/* = NULL*/)
1456 {
1457 	if (withUndo)
1458 		prepareUndo();
1459 
1460 	PatternTools patternTools;
1461 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1462 
1463 	// clear out entire effect + operand
1464 	if (value == 0xFF)
1465 	{
1466 		patternTools.setEffect(1, 0, 0);
1467 
1468 		if (advanceImpl)
1469 			advanceImpl->advance();
1470 
1471 		if (withUndo)
1472 			finishUndo(LastChangeSlotChange);
1473 		return true;
1474 	}
1475 
1476 	pp_int32 eff,op;
1477 
1478 	// skip first effect (= volume command)
1479 	patternTools.getFirstEffect(eff, op);
1480 	patternTools.getNextEffect(eff, op);
1481 
1482 	patternTools.convertEffectsToFT2(eff, op);
1483 
1484 	pp_int32 newEff = value;
1485 	patternTools.convertEffectsFromFT2(newEff, op);
1486 
1487 	if (newEff == 0x40)
1488 	{
1489 		newEff = 0x41;
1490 		op &= 0x0F;
1491 	}
1492 	else if (newEff >= 0x43)
1493 	{
1494 		newEff = 0x42;
1495 		op &= 0x0F;
1496 	}
1497 
1498 	patternTools.setEffect(1, newEff, op);
1499 
1500 	if (advanceImpl)
1501 		advanceImpl->advance();
1502 
1503 	if (withUndo)
1504 		finishUndo(LastChangeSlotChange);
1505 	return true;
1506 }
1507 
writeEffectOperand(NibbleTypes nibleType,pp_uint8 value,bool withUndo,PatternAdvanceInterface * advanceImpl)1508 bool PatternEditor::writeEffectOperand(NibbleTypes nibleType, pp_uint8 value, bool withUndo/* = false*/, PatternAdvanceInterface* advanceImpl/* = NULL*/)
1509 {
1510 	if (withUndo)
1511 		prepareUndo();
1512 
1513 	PatternTools patternTools;
1514 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1515 
1516 	// clear out entire effect + operand
1517 	if (value == 0xFF)
1518 	{
1519 		patternTools.setEffect(1, 0, 0);
1520 
1521 		if (advanceImpl)
1522 			advanceImpl->advance();
1523 
1524 		if (withUndo)
1525 			finishUndo(LastChangeSlotChange);
1526 		return true;
1527 	}
1528 
1529 	pp_int32 eff,op;
1530 
1531 	patternTools.getFirstEffect(eff, op);
1532 	patternTools.getNextEffect(eff, op);
1533 
1534 	patternTools.convertEffectsToFT2(eff, op);
1535 
1536 	if (nibleType == NibbleTypeHigh)
1537 	{
1538 		op &= 0x0F;
1539 		op |= (pp_int32)value << 4;
1540 	}
1541 	else if (nibleType == NibbleTypeLow)
1542 	{
1543 		op &= 0xF0;
1544 		op |= (pp_int32)value;
1545 	}
1546 
1547 	patternTools.convertEffectsFromFT2(eff, op);
1548 
1549 	if (eff == 0x40)
1550 		eff = 0x41;
1551 	if (eff >= 0x43)
1552 		eff = 0x42;
1553 
1554 	patternTools.setEffect(1, eff, op);
1555 
1556 	if (advanceImpl)
1557 		advanceImpl->advance();
1558 
1559 	if (withUndo)
1560 		finishUndo(LastChangeSlotChange);
1561 	return true;
1562 }
1563 
storeMacroFromCursor(pp_int32 slot)1564 void PatternEditor::storeMacroFromCursor(pp_int32 slot)
1565 {
1566 	if (pattern == NULL)
1567 		return;
1568 
1569 	if (slot > 10 || slot < 0)
1570 		return;
1571 
1572 	PatternTools patternTools;
1573 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1574 
1575 	pp_int32 eff, op;
1576 
1577 	if (cursor.inner >= 3 && cursor.inner <= 4)
1578 	{
1579 		patternTools.getEffect(0, eff, op);
1580 		// feature shall also work with empty effects
1581 		//if (!eff && !op)
1582 		//	return;
1583 		effectMacros[slot].effect = (pp_uint8)eff;
1584 		effectMacros[slot].operand = (pp_uint8)op;
1585 	}
1586 	else if (cursor.inner > 4)
1587 	{
1588 		patternTools.getEffect(1, eff, op);
1589 		// feature shall also work with empty effects
1590 		//if (!eff && !op)
1591 		//	return;
1592 		effectMacros[slot+10].effect = (pp_uint8)eff;
1593 		effectMacros[slot+10].operand = (pp_uint8)op;
1594 	}
1595 }
1596 
writeMacroToCursor(pp_int32 slot,PatternAdvanceInterface * advanceImpl)1597 void PatternEditor::writeMacroToCursor(pp_int32 slot, PatternAdvanceInterface* advanceImpl/* = NULL*/)
1598 {
1599 	if (pattern == NULL)
1600 		return;
1601 
1602 	if (slot > 10 || slot < 0)
1603 		return;
1604 
1605 	prepareUndo();
1606 
1607 	PatternTools patternTools;
1608 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1609 
1610 	if (cursor.inner >= 3 && cursor.inner <= 4)
1611 	{
1612 		// feature shall also work with empty effects
1613 		//if (!effectMacros[slot].effect && !effectMacros[slot].operand)
1614 		//	goto writeNothing;
1615 		patternTools.setEffect(0, effectMacros[slot].effect, effectMacros[slot].operand);
1616 	}
1617 	else if (cursor.inner > 4)
1618 	{
1619 		// feature shall also work with empty effects
1620 		//if (!effectMacros[slot+10].effect && !effectMacros[slot+10].operand)
1621 		//	goto writeNothing;
1622 		patternTools.setEffect(1, effectMacros[slot+10].effect, effectMacros[slot+10].operand);
1623 	}
1624 
1625 	if (advanceImpl)
1626 		advanceImpl->advance();
1627 
1628 //writeNothing:
1629 
1630 	finishUndo(LastChangeWriteMacro);
1631 }
1632 
getMacroOperands(pp_int32 slot,pp_uint8 & eff,pp_uint8 & op)1633 void PatternEditor::getMacroOperands(pp_int32 slot, pp_uint8& eff, pp_uint8& op)
1634 {
1635 	if (slot < 0 || slot >= 20)
1636 		return;
1637 
1638 	eff = effectMacros[slot].effect;
1639 	op = effectMacros[slot].operand;
1640 }
1641 
setMacroOperands(pp_int32 slot,pp_uint8 eff,pp_uint8 op)1642 void PatternEditor::setMacroOperands(pp_int32 slot, pp_uint8 eff, pp_uint8 op)
1643 {
1644 	if (slot < 0 || slot >= 20)
1645 		return;
1646 
1647 	effectMacros[slot].effect = eff;
1648 	effectMacros[slot].operand = op;
1649 }
1650 
deleteCursorSlotData(PatternAdvanceInterface * advanceImpl)1651 void PatternEditor::deleteCursorSlotData(PatternAdvanceInterface* advanceImpl/* = NULL*/)
1652 {
1653 	prepareUndo();
1654 	PatternTools patternTools;
1655 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1656 	if (cursor.inner == 3 || cursor.inner == 4)
1657 	{
1658 		patternTools.setFirstEffect(0,0);
1659 	}
1660 	else if (cursor.inner == 5 || cursor.inner == 6 || cursor.inner == 7)
1661 	{
1662 		// What have I been thinking?
1663 		//pp_int32 eff, op;
1664 		//patternTools.getEffect(1, eff, op);
1665 		// actually I think delete should really delete
1666 		patternTools.setEffect(1, 0, 0);
1667 	}
1668 	else
1669 	{
1670 		patternTools.setNote(0);
1671 		patternTools.setInstrument(0);
1672 	}
1673 
1674 	if (advanceImpl)
1675 		advanceImpl->advance();
1676 
1677 	finishUndo(LastChangeDeleteNote);
1678 }
1679 
deleteCursorSlotDataEntire(PatternAdvanceInterface * advanceImpl)1680 void PatternEditor::deleteCursorSlotDataEntire(PatternAdvanceInterface* advanceImpl/* = NULL*/)
1681 {
1682 	prepareUndo();
1683 	PatternTools patternTools;
1684 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1685 	patternTools.setNote(0);
1686 	patternTools.setInstrument(0);
1687 	patternTools.setFirstEffect(0,0);
1688 	patternTools.setNextEffect(0,0);
1689 
1690 	if (advanceImpl)
1691 		advanceImpl->advance();
1692 
1693 	finishUndo(LastChangeDeleteNoteVolumeAndEffect);
1694 }
1695 
deleteCursorSlotDataVolumeAndEffect(PatternAdvanceInterface * advanceImpl)1696 void PatternEditor::deleteCursorSlotDataVolumeAndEffect(PatternAdvanceInterface* advanceImpl/* = NULL*/)
1697 {
1698 	prepareUndo();
1699 	PatternTools patternTools;
1700 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1701 	patternTools.setFirstEffect(0,0);
1702 	patternTools.setNextEffect(0,0);
1703 
1704 	if (advanceImpl)
1705 		advanceImpl->advance();
1706 
1707 	finishUndo(LastChangeDeleteVolumeAndEffect);
1708 }
1709 
deleteCursorSlotDataEffect(PatternAdvanceInterface * advanceImpl)1710 void PatternEditor::deleteCursorSlotDataEffect(PatternAdvanceInterface* advanceImpl/* = NULL*/)
1711 {
1712 	prepareUndo();
1713 	PatternTools patternTools;
1714 	patternTools.setPosition(pattern, cursor.channel, cursor.row);
1715 	pp_int32 eff, op;
1716 	patternTools.getFirstEffect(eff, op);
1717 	patternTools.setFirstEffect(eff,op);
1718 	patternTools.setNextEffect(0,0);
1719 
1720 	if (advanceImpl)
1721 		advanceImpl->advance();
1722 
1723 	finishUndo(LastChangeDeleteEffect);
1724 }
1725 
insertNote(pp_int32 channel,pp_int32 row)1726 void PatternEditor::insertNote(pp_int32 channel, pp_int32 row)
1727 {
1728 	if (pattern == NULL || pattern->patternData == NULL)
1729 		return;
1730 
1731 	if (channel < 0 || channel >= pattern->channum)
1732 		return;
1733 
1734 	prepareUndo();
1735 
1736 	mp_sint32 slotSize = pattern->effnum * 2 + 2;
1737 	mp_sint32 rowSize = slotSize*pattern->channum;
1738 
1739 	for (mp_sint32 i = pattern->rows - 1; i > row; i--)
1740 	{
1741 		mp_ubyte* src = (i-1)*rowSize + channel*slotSize + pattern->patternData;
1742 		mp_ubyte* dst = i*rowSize + channel*slotSize + pattern->patternData;
1743 
1744 		memcpy(dst, src, slotSize);
1745 	}
1746 
1747 	memset(row*rowSize + channel*slotSize + pattern->patternData, 0, slotSize);
1748 
1749 	finishUndo(LastChangeInsertNote);
1750 }
1751 
insertLine(pp_int32 row)1752 void PatternEditor::insertLine(pp_int32 row)
1753 {
1754 	if (pattern == NULL || pattern->patternData == NULL)
1755 		return;
1756 
1757 	if (row < 0 || row >= pattern->rows)
1758 		return;
1759 
1760 	prepareUndo();
1761 
1762 	mp_sint32 slotSize = pattern->effnum * 2 + 2;
1763 
1764 	mp_sint32 rowSize = slotSize*pattern->channum;
1765 
1766 	for (mp_sint32 i = pattern->rows - 1; i > row; i--)
1767 	{
1768 		mp_ubyte* src = (i-1)*rowSize + pattern->patternData;
1769 		mp_ubyte* dst = i*rowSize + pattern->patternData;
1770 
1771 		memcpy(dst, src, rowSize);
1772 	}
1773 
1774 	memset(row*rowSize + pattern->patternData, 0, rowSize);
1775 
1776 	finishUndo(LastChangeInsertLine);
1777 }
1778 
deleteNote(pp_int32 channel,pp_int32 row)1779 void PatternEditor::deleteNote(pp_int32 channel, pp_int32 row)
1780 {
1781 	if (pattern == NULL || pattern->patternData == NULL)
1782 		return;
1783 
1784 	if (row < 0 || row >= pattern->rows)
1785 		return;
1786 
1787 	if (channel < 0 || channel >= pattern->channum)
1788 		return;
1789 
1790 	prepareUndo();
1791 
1792 	mp_sint32 slotSize = pattern->effnum * 2 + 2;
1793 	mp_sint32 rowSize = slotSize*pattern->channum;
1794 
1795 	for (mp_sint32 i = row; i < pattern->rows-1; i++)
1796 	{
1797 		mp_ubyte* src = (i+1)*rowSize + channel*slotSize + pattern->patternData;
1798 		mp_ubyte* dst = i*rowSize + channel*slotSize + pattern->patternData;
1799 
1800 		memcpy(dst, src, slotSize);
1801 	}
1802 
1803 	memset((pattern->rows-1)*rowSize + channel*slotSize + pattern->patternData, 0, slotSize);
1804 
1805 	finishUndo(LastChangeDeleteNote);
1806 }
1807 
deleteLine(pp_int32 row)1808 void PatternEditor::deleteLine(pp_int32 row)
1809 {
1810 	if (pattern == NULL || pattern->patternData == NULL)
1811 		return;
1812 
1813 	if (row < 0 || row >= pattern->rows)
1814 		return;
1815 
1816 	prepareUndo();
1817 
1818 	mp_sint32 slotSize = pattern->effnum * 2 + 2;
1819 
1820 	mp_sint32 rowSize = slotSize*pattern->channum;
1821 
1822 	for (mp_sint32 i = row; i < pattern->rows-1; i++)
1823 	{
1824 		mp_ubyte* src = (i+1)*rowSize + pattern->patternData;
1825 		mp_ubyte* dst = i*rowSize + pattern->patternData;
1826 
1827 		memcpy(dst, src, rowSize);
1828 	}
1829 
1830 	memset((pattern->rows-1)*rowSize + pattern->patternData, 0, rowSize);
1831 
1832 	finishUndo(LastChangeDeleteLine);
1833 }
1834 
1835 
moveSelection(pp_int32 channels,pp_int32 rows)1836 void PatternEditor::moveSelection(pp_int32 channels, pp_int32 rows)
1837 {
1838 	PatternEditorTools::Position targetStart = selection.start;
1839 	PatternEditorTools::Position targetEnd = selection.end;
1840 	targetStart.row += rows;
1841 	targetStart.channel += channels;
1842 	targetEnd.row += rows;
1843 	targetEnd.channel += channels;
1844 
1845 	if (!PatternEditorTools::hasValidSelection(pattern, selection.start, selection.end))
1846 		return;
1847 	if (!PatternEditorTools::hasValidSelection(pattern, targetStart, targetEnd))
1848 		return;
1849 
1850 	prepareUndo();
1851 
1852 	PatternEditorTools tools(pattern);
1853 	tools.moveSelection(selection.start, selection.end, channels, rows, true);
1854 
1855 	selection.start = targetStart;
1856 	selection.end = targetEnd;
1857 
1858 	finishUndo(LastChangeMoveSelection);
1859 }
1860 
1861 
cloneSelection(pp_int32 channels,pp_int32 rows)1862 void PatternEditor::cloneSelection(pp_int32 channels, pp_int32 rows)
1863 {
1864 	PatternEditorTools::Position targetStart = selection.start;
1865 	PatternEditorTools::Position targetEnd = selection.end;
1866 	targetStart.row += rows;
1867 	targetStart.channel += channels;
1868 	targetEnd.row += rows;
1869 	targetEnd.channel += channels;
1870 
1871 	if (!PatternEditorTools::hasValidSelection(pattern, selection.start, selection.end))
1872 		return;
1873 	if (!PatternEditorTools::hasValidSelection(pattern, targetStart, targetEnd))
1874 		return;
1875 
1876 	prepareUndo();
1877 
1878 	PatternEditorTools tools(pattern);
1879 	tools.moveSelection(selection.start, selection.end, channels, rows, false);  // don't erase source notes
1880 
1881 	selection.start = targetStart;
1882 	selection.end = targetEnd;
1883 
1884 	finishUndo(LastChangeCloneSelection);
1885 }