1 /*
2  *  tracker/PatternEditor.h
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.h
25  *  MilkyTracker
26  *
27  *  Created by Peter Barth on 16.11.07.
28  *
29  */
30 
31 #ifndef __PATTERNEDITOR_H__
32 #define __PATTERNEDITOR_H__
33 
34 #include "EditorBase.h"
35 #include "PatternEditorTools.h"
36 #include "Undo.h"
37 
38 struct TXMPattern;
39 class XModule;
40 
41 class PatternEditor : public EditorBase
42 {
43 public:
44 	// These are the clipboards, FT2 uses different clipboard buffers for
45 	// copy/cut/paste block/track/pattern operations, so we imitate these too
46 	enum ClipBoardTypes
47 	{
48 		ClipBoardTypeSelection,
49 		ClipBoardTypeTrack,
50 		ClipBoardTypePattern,
51 		// do not use
52 		ClipBoardTypeLAST
53 	};
54 
55 	class PatternAdvanceInterface
56 	{
57 	public:
58 		virtual void advance() = 0;
59 	};
60 
61 	class Selection
62 	{
63 	private:
64 		bool copyValid;
65 
66 	public:
67 		PatternEditorTools::Position start, end, startCopy, endCopy;
68 
Selection()69 		Selection()
70 		{
71 			reset();
72 			startCopy = start;
73 			endCopy = end;
74 			copyValid = false;
75 		}
76 
reset()77 		void reset()
78 		{
79 			start.channel = -1;
80 			start.row = -1;
81 			start.inner = 0;
82 			end.channel = -1;
83 			end.row = -1;
84 			end.inner = 7;
85 		}
86 
isValid()87 		bool isValid()
88 		{
89 			return (start.channel >= 0 && start.row >= 0 &&
90 					end.channel >= 0 && end.row >= 0);
91 		}
92 
isCopyValid()93 		bool isCopyValid()
94 		{
95 			return (startCopy.channel >= 0 && startCopy.row >= 0 &&
96 					endCopy.channel >= 0 && endCopy.row >= 0) && copyValid;
97 		}
98 
99 		void backup();
100 
101 		void restore();
102 	};
103 
104 	struct TCommand
105 	{
106 		pp_uint8 effect;
107 		pp_uint8 operand;
108 	};
109 
110 private:
111 	class ClipBoard
112 	{
113 	private:
114 		mp_ubyte* buffer;
115 
116 		PatternEditorTools::Position selectionStart, selectionEnd;
117 
118 		pp_int32 selectionWidth;
119 		pp_int32 selectionHeight;
120 
121 		// FT2 uses different clipboards for track/pattern/block operations
122 		// so a regular singleton design won't cut it
123 		static ClipBoard* instances[ClipBoardTypeLAST];
124 
125 		ClipBoard();
126 
127 	public:
128 		~ClipBoard();
129 
130 		static ClipBoard* getInstance(ClipBoardTypes type);
131 
132 		void makeCopy(TXMPattern& pattern,
133 					  const PatternEditorTools::Position& ss, const PatternEditorTools::Position& se,
134 					  bool clear = false);
135 		void paste(TXMPattern& pattern, pp_int32 sc, pp_int32 sr, bool transparent = false);
isEmpty()136 		bool isEmpty() const { return buffer == NULL; }
137 
getNumRows()138 		pp_int32 getNumRows() { return selectionHeight; }
getNumChannels()139 		pp_int32 getNumChannels() { return selectionWidth; }
140 	};
141 
142 	// operations
143 	enum LastChanges
144 	{
145 		LastChangeNone,
146 		LastChangeSlotChange,
147 		LastChangeInsertNote,
148 		LastChangeInsertLine,
149 		LastChangeDeleteNote,
150 		LastChangeDeleteLine,
151 		LastChangeDeleteNoteVolumeAndEffect,
152 		LastChangeDeleteVolumeAndEffect,
153 		LastChangeDeleteEffect,
154 		LastChangeWriteMacro,
155 		LastChangeCut,
156 		LastChangePaste,
157 		LastChangeDeleteSelection,
158 		LastChangeMoveSelection,
159 		LastChangeCloneSelection,
160 
161 		LastChangeExpandPattern,
162 		LastChangeShrinkPattern,
163 		LastChangeResizePattern,
164 
165 		LastChangeLoadXPattern,
166 		LastChangeLoadXTrack,
167 
168 		LastChangeInsIncSelection,
169 		LastChangeInsDecSelection,
170 		LastChangeInsIncTrack,
171 		LastChangeInsDecTrack,
172 		LastChangeInsRemapTrack,
173 		LastChangeInsRemapPattern,
174 		LastChangeInsRemapSelection,
175 
176 		LastChangeNoteTransposeTrack,
177 		LastChangeNoteTransposePattern,
178 		LastChangeNoteTransposeSelection,
179 
180 		LastChangeNoteInterpolate,
181 		LastChangeSplitTrack,
182 		LastChangeSwapChannels,
183 		LastChangeScaleVolume,
184 
185 		LastChangeZeroOperandsTrack,
186 		LastChangeZeroOperandsPattern,
187 		LastChangeZeroOperandsSelection,
188 
189 		LastChangeFillOperandsTrack,
190 		LastChangeFillOperandsPattern,
191 		LastChangeFillOperandsSelection,
192 
193 		LastChangeRelocateCommandsTrack,
194 		LastChangeRelocateCommandsPattern,
195 		LastChangeRelocateCommandsSelection
196 	};
197 
198 private:
199 	TXMPattern* pattern;
200 
201 	// Current cursor position
202 	PatternEditorTools::Position cursor;
203 	// Current selection
204 	Selection selection;
205 
206 	pp_int32 numVisibleChannels;
207 	bool autoResize;
208 	pp_int32 currentInstrument;
209 	bool instrumentEnabled;
210 	bool instrumentBackTrace;
211 	pp_int32 currentOctave;
212 
213 	// undo/redo information
214 	UndoStackEntry::UserData undoUserData;
215 	PatternUndoStackEntry* before;
216 	PPUndoStack<PatternUndoStackEntry>* undoStack;
217 	UndoHistory<TXMPattern, PatternUndoStackEntry>* undoHistory;
218 	LastChanges lastChange;
219 	bool lastOperationDidChangeRows;
220 	bool lastOperationDidChangeCursor;
221 
222 	TCommand effectMacros[20];
223 
224 	void prepareUndo();
225 	bool finishUndo(LastChanges lastChange, bool nonRepeat = false);
226 
227 	bool revoke(const PatternUndoStackEntry* stackEntry);
228 
229 	void cut(ClipBoard& clipBoard);
230 	void copy(ClipBoard& clipBoard);
231 	void paste(ClipBoard& clipBoard, bool transparent = false, pp_int32 fromChannel = -1);
232 
233 	void clearRange(const PatternEditorTools::Position& rangeStart, const PatternEditorTools::Position& rangeEnd);
234 
235 public:
236 	PatternEditor();
237 	virtual ~PatternEditor();
238 
239 	// query status
getLastOperationDidChangeRows()240 	bool getLastOperationDidChangeRows() const { return lastOperationDidChangeRows; }
getLastOperationDidChangeCursor()241 	bool getLastOperationDidChangeCursor() const { return lastOperationDidChangeCursor; }
242 
243 	void attachPattern(TXMPattern* pattern, XModule* module);
getPattern()244 	TXMPattern* getPattern() { return pattern; }
245 
246 	void reset();
247 
248 	pp_int32 getNumChannels() const;
249 	pp_int32 getNumRows() const;
250 
setNumVisibleChannels(pp_int32 numVisibleChannels)251 	void setNumVisibleChannels(pp_int32 numVisibleChannels) { this->numVisibleChannels = numVisibleChannels; }
setAutoResize(bool autoResize)252 	void setAutoResize(bool autoResize) { this->autoResize = autoResize; }
getAutoResize()253 	bool getAutoResize() const { return autoResize; }
254 
255 	// dealing with current cursor
setCursor(const PatternEditorTools::Position & cursor)256 	void setCursor(const PatternEditorTools::Position& cursor) { this->cursor = cursor; }
getCursor()257 	PatternEditorTools::Position& getCursor() { return cursor; }
258 
resetCursor()259 	void resetCursor() { cursor.row = cursor.channel = cursor.inner = 0; }
260 
261 	// dealing with the selection
setSelection(const Selection & selection)262 	void setSelection(const Selection& selection) { this->selection = selection; }
getSelection()263 	Selection& getSelection() { return selection; }
setSelectionStart(const PatternEditorTools::Position & pos)264 	void setSelectionStart(const PatternEditorTools::Position& pos) { selection.start = pos; }
setSelectionEnd(const PatternEditorTools::Position & pos)265 	void setSelectionEnd(const PatternEditorTools::Position& pos) { selection.end = pos; }
resetSelection()266 	void resetSelection() { selection.reset(); }
267 	bool hasValidSelection();
268 	bool canMoveSelection(pp_int32 channels, pp_int32 rows);
269 	bool selectionContains(const PatternEditorTools::Position& pos);
270 	void selectChannel(pp_int32 channel);
271 	void selectAll();
272 
273 	// dealing with instrument
setCurrentInstrument(pp_int32 ins)274 	void setCurrentInstrument(pp_int32 ins) { currentInstrument = ins; }
275 	pp_int32 getCurrentActiveInstrument();
enableInstrument(bool instrumentEnabled)276 	void enableInstrument(bool instrumentEnabled) { this->instrumentEnabled = instrumentEnabled; }
isInstrumentEnabled()277 	bool isInstrumentEnabled() { return instrumentEnabled; }
278 	// Intelligent instrument backtrace?
setInstrumentBackTrace(bool instrumentBackTrace)279 	void setInstrumentBackTrace(bool instrumentBackTrace) { this->instrumentBackTrace = instrumentBackTrace; }
280 
setCurrentOctave(pp_int32 octave)281 	void setCurrentOctave(pp_int32 octave) { currentOctave = octave; }
getCurrentOctave()282 	pp_int32 getCurrentOctave() const { return currentOctave; }
increaseCurrentOctave()283 	void increaseCurrentOctave() { if (currentOctave < 8) currentOctave++; }
decreaseCurrentOctave()284 	void decreaseCurrentOctave() { if (currentOctave > 1) currentOctave--; }
285 
286 	// --- Multilevel UNDO / REDO --------------------------------------------
canUndo()287 	bool canUndo() const { if (undoStack) return !undoStack->IsEmpty(); else return false; }
canRedo()288 	bool canRedo() const { if (undoStack) return !undoStack->IsTop(); else return false; }
289 	// undo last changes
290 	bool undo();
291 	// redo last changes
292 	bool redo();
setUndoUserData(const void * data,pp_uint32 dataLen)293 	void setUndoUserData(const void* data, pp_uint32 dataLen) { this->undoUserData = UndoStackEntry::UserData((pp_uint8*)data, dataLen); }
getUndoUserDataLen()294 	pp_uint32 getUndoUserDataLen() const { return undoUserData.getDataLen(); }
getUndoUserData()295 	const void* getUndoUserData() const { return (void*)undoUserData.getData(); }
296 
297 	// --- dealing with the pattern data -------------------------------------
298 	void clearSelection();
299 
300 	void clearPattern();
301 
clipBoardSelectionIsEmpty()302 	bool clipBoardSelectionIsEmpty() const { return ClipBoard::getInstance(ClipBoardTypeSelection)->isEmpty(); }
303 
304 	void cut(ClipBoardTypes clipBoardType);
305 	void copy(ClipBoardTypes clipBoardType);
306 	void paste(ClipBoardTypes clipBoardType, bool transparent = false, pp_int32 fromChannel = -1);
307 
308 	// resize pattern to a new number of rows
309 	bool resizePattern(pp_int32 newRowNum, bool withUndo = true);
310 
311 	// insert a blank line after each pattern line
312 	bool expandPattern();
313 
314 	// delete every odd pattern line
315 	bool shrinkPattern();
316 
317 	// Load extended pattern from file (.XP)
318 	bool loadExtendedPattern(const PPSystemString& fileName);
319 	bool saveExtendedPattern(const PPSystemString& fileName);
320 
321 	// Load extended track from file (.XT)
322 	bool loadExtendedTrack(const PPSystemString& fileName);
323 	bool saveExtendedTrack(const PPSystemString& fileName);
324 
325 	// --- increase/decrease/remap instruments -------------------------------------------------
326 	pp_int32 insIncSelection();
327 	pp_int32 insDecSelection();
328 	pp_int32 insIncTrack();
329 	pp_int32 insDecTrack();
330 	pp_int32 insRemapTrack(pp_int32 oldIns, pp_int32 newIns);
331 	pp_int32 insRemapPattern(pp_int32 oldIns, pp_int32 newIns);
332 	pp_int32 insRemapSelection(pp_int32 oldIns, pp_int32 newIns);
333 
334 	// --- transpose notes ---------------------------------------------------
335 	pp_int32 noteTransposeTrackCore(const PatternEditorTools::TransposeParameters& transposeParameters, bool evaluate);
336 	pp_int32 noteTransposePatternCore(const PatternEditorTools::TransposeParameters& transposeParameters, bool evaluate);
337 	pp_int32 noteTransposeSelectionCore(const PatternEditorTools::TransposeParameters& transposeParameters, bool evaluate);
338 
339 	// --- interpolate values in current selection ---------------------------
340 	pp_int32 interpolateValuesInSelection();
341 
342 	// --- split track -------------------------------------------------------
343 	pp_int32 splitTrack(pp_int32 useChannels, bool selectionOnly, bool insertNoteOff);
344 
345 	// --- swap channels -----------------------------------------------------
346 	pp_int32 swapChannels(pp_int32 dstChannel, pp_int32 srcChannel);
347 
348 	// --- FT2 compatible scale volume function ------------------------------
349 	pp_int32 scaleVolume(const PatternEditorTools::Position& startSelection, const PatternEditorTools::Position& endSelection, float startScale, float endScale);
350 	pp_int32 scaleVolumeTrack(float startScale, float endScale);
351 	pp_int32 scaleVolumePattern(float startScale, float endScale);
352 	pp_int32 scaleVolumeSelection(float startScale, float endScale);
353 
354 	// --- Zero out unecessary operands --------------------------------------
355 	pp_int32 zeroOperandsTrack(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
356 	pp_int32 zeroOperandsPattern(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
357 	pp_int32 zeroOperandsSelection(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
358 
359 	// --- Fill in zero operands ---------------------------------------------
360 	pp_int32 fillOperandsTrack(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
361 	pp_int32 fillOperandsPattern(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
362 	pp_int32 fillOperandsSelection(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
363 
364 	// --- Relocate FX into volume column if possible ------------------------
365 	pp_int32 relocateCommandsTrack(const PatternEditorTools::RelocateParameters& relocateParameters, bool evaluate);
366 	pp_int32 relocateCommandsPattern(const PatternEditorTools::RelocateParameters& relocateParameters, bool evaluate);
367 	pp_int32 relocateCommandsSelection(const PatternEditorTools::RelocateParameters& relocateParameters, bool evaluate);
368 
369 	// --- write slot data ---------------------------------------------------
370 	bool writeNote(pp_int32 note,
371 				   bool withUndo = false,
372 				   PatternAdvanceInterface* advanceImpl = NULL);
373 
374 	// --- write through, without undo etc. ----------------------------------
375 	void writeDirectNote(pp_int32 note,
376 						 pp_int32 track = -1,
377 						 pp_int32 row = -1,
378 						 pp_int32 order = -1);
379 
380 	enum NibbleTypes
381 	{
382 		NibbleTypeLow,
383 		NibbleTypeHigh,
384 		NibbleTypeBoth
385 	};
386 
387 	bool writeInstrument(NibbleTypes nibleType, pp_uint8 value, bool withUndo = false, PatternAdvanceInterface* advanceImpl = NULL);
388 	bool writeFT2Volume(NibbleTypes nibleType, pp_uint8 value, bool withUndo = false, PatternAdvanceInterface* advanceImpl = NULL);
389 	bool writeEffectNumber(pp_uint8 value, bool withUndo = false, PatternAdvanceInterface* advanceImpl = NULL);
390 	bool writeEffectOperand(NibbleTypes nibleType, pp_uint8 value, bool withUndo = false, PatternAdvanceInterface* advanceImpl = NULL);
391 
392 	bool writeEffect(pp_int32 effNum, pp_uint8 eff, pp_uint8 op,
393 					 bool withUndo = false,
394 					 PatternAdvanceInterface* advanceImpl = NULL);
395 
396 	// --- write through, without undo etc. ----------------------------------
397 	void writeDirectEffect(pp_int32 effNum, pp_uint8 eff, pp_uint8 op,
398 						   pp_int32 track = -1,
399 						   pp_int32 row = -1,
400 						   pp_int32 order = -1);
401 
402 	// --- dealing with FT2 style effect macros ------------------------------
403 	void storeMacroFromCursor(pp_int32 slot);
404 	void writeMacroToCursor(pp_int32 slot, PatternAdvanceInterface* advanceImpl = NULL);
405 
406 	void getMacroOperands(pp_int32 slot, pp_uint8& eff, pp_uint8& op);
407 	void setMacroOperands(pp_int32 slot, pp_uint8 eff, pp_uint8 op);
408 
409 	// --- deleting slot data ------------------------------------------------
410 	void deleteCursorSlotData(PatternAdvanceInterface* advanceImpl = NULL);
411 	void deleteCursorSlotDataEntire(PatternAdvanceInterface* advanceImpl = NULL);
412 	void deleteCursorSlotDataVolumeAndEffect(PatternAdvanceInterface* advanceImpl = NULL);
413 	void deleteCursorSlotDataEffect(PatternAdvanceInterface* advanceImpl = NULL);
414 
415 	// --- inserting/deleting entire rows ------------------------------------
416 	void insertNote(pp_int32 channel, pp_int32 row);
417 	void insertLine(pp_int32 row);
418 	void deleteNote(pp_int32 channel, pp_int32 row);
419 	void deleteLine(pp_int32 row);
420 
421 	// --- moving entire selection -------------------------------------------
422 	void moveSelection(pp_int32 channels, pp_int32 rows);
423 	void cloneSelection(pp_int32 channels, pp_int32 rows);
424 
425 };
426 
427 #endif
428