mirror of
https://github.com/tildearrow/furnace.git
synced 2024-10-31 18:12:40 +00:00
GUI: collapse/expand pattern/song
This commit is contained in:
parent
f3a71c6eba
commit
24487936de
6 changed files with 315 additions and 59 deletions
|
@ -23,11 +23,7 @@
|
|||
static DivPattern emptyPat;
|
||||
|
||||
DivPattern::DivPattern() {
|
||||
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int i=0; i<DIV_MAX_ROWS; i++) {
|
||||
data[i][0]=0;
|
||||
data[i][1]=0;
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
DivPattern* DivChannelData::getPattern(int index, bool create) {
|
||||
|
@ -93,6 +89,14 @@ void DivPattern::copyOn(DivPattern* dest) {
|
|||
memcpy(dest->data,data,sizeof(data));
|
||||
}
|
||||
|
||||
void DivPattern::clear() {
|
||||
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int i=0; i<DIV_MAX_ROWS; i++) {
|
||||
data[i][0]=0;
|
||||
data[i][1]=0;
|
||||
}
|
||||
}
|
||||
|
||||
DivChannelData::DivChannelData():
|
||||
effectCols(1) {
|
||||
memset(data,0,DIV_MAX_PATTERNS*sizeof(void*));
|
||||
|
|
|
@ -24,6 +24,11 @@ struct DivPattern {
|
|||
String name;
|
||||
short data[DIV_MAX_ROWS][DIV_MAX_COLS];
|
||||
|
||||
/**
|
||||
* clear the pattern.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* copy this pattern to another.
|
||||
* @param dest the destination pattern.
|
||||
|
|
|
@ -561,18 +561,32 @@ void FurnaceGUI::doAction(int what) {
|
|||
doFlip();
|
||||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE_ROWS:
|
||||
doCollapse(2);
|
||||
doCollapse(2,selStart,selEnd);
|
||||
break;
|
||||
case GUI_ACTION_PAT_EXPAND_ROWS:
|
||||
doExpand(2);
|
||||
doExpand(2,selStart,selEnd);
|
||||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE_PAT: // TODO
|
||||
case GUI_ACTION_PAT_COLLAPSE_PAT: {
|
||||
SelectionPoint selEndPat;
|
||||
selEndPat.xCoarse=e->getTotalChannelCount()-1;
|
||||
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
|
||||
selEndPat.y=e->curSubSong->patLen-1;
|
||||
doCollapse(2,SelectionPoint(0,0,0),selEndPat);
|
||||
break;
|
||||
case GUI_ACTION_PAT_EXPAND_PAT: // TODO
|
||||
}
|
||||
case GUI_ACTION_PAT_EXPAND_PAT: {
|
||||
SelectionPoint selEndPat;
|
||||
selEndPat.xCoarse=e->getTotalChannelCount()-1;
|
||||
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
|
||||
selEndPat.y=e->curSubSong->patLen-1;
|
||||
doExpand(2,SelectionPoint(0,0,0),selEndPat);
|
||||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE_SONG: // TODO
|
||||
}
|
||||
case GUI_ACTION_PAT_COLLAPSE_SONG:
|
||||
doCollapseSong(2);
|
||||
break;
|
||||
case GUI_ACTION_PAT_EXPAND_SONG: // TODO
|
||||
case GUI_ACTION_PAT_EXPAND_SONG:
|
||||
doExpandSong(2);
|
||||
break;
|
||||
case GUI_ACTION_PAT_LATCH: // TODO
|
||||
break;
|
||||
|
|
|
@ -67,6 +67,9 @@ void FurnaceGUI::prepareUndo(ActionType action) {
|
|||
e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false)->copyOn(oldPat[i]);
|
||||
}
|
||||
break;
|
||||
case GUI_UNDO_PATTERN_COLLAPSE_SONG:
|
||||
case GUI_UNDO_PATTERN_EXPAND_SONG: // TODO
|
||||
break;
|
||||
case GUI_UNDO_REPLACE: // this is handled by doReplace()
|
||||
break;
|
||||
}
|
||||
|
@ -130,6 +133,9 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
|||
doPush=true;
|
||||
}
|
||||
break;
|
||||
case GUI_UNDO_PATTERN_COLLAPSE_SONG:
|
||||
case GUI_UNDO_PATTERN_EXPAND_SONG: // TODO
|
||||
break;
|
||||
case GUI_UNDO_REPLACE: // this is handled by doReplace()
|
||||
break;
|
||||
}
|
||||
|
@ -839,50 +845,56 @@ void FurnaceGUI::doFlip() {
|
|||
makeUndo(GUI_UNDO_PATTERN_FLIP);
|
||||
}
|
||||
|
||||
void FurnaceGUI::doCollapse(int divider) {
|
||||
void FurnaceGUI::doCollapse(int divider, const SelectionPoint& sStart, const SelectionPoint& sEnd) {
|
||||
if (divider<2) return;
|
||||
if (e->curSubSong->patLen<divider) {
|
||||
showError("can't collapse any further!");
|
||||
return;
|
||||
}
|
||||
|
||||
finishSelection();
|
||||
prepareUndo(GUI_UNDO_PATTERN_COLLAPSE);
|
||||
|
||||
DivPattern patBuffer;
|
||||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
int iCoarse=sStart.xCoarse;
|
||||
int iFine=sStart.xFine;
|
||||
for (; iCoarse<=sEnd.xCoarse; iCoarse++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
|
||||
maskOut(opMaskCollapseExpand,iFine);
|
||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||
for (int j=sStart.y; j<=sEnd.y; j++) {
|
||||
if (iFine==0) {
|
||||
patBuffer.data[j][0]=pat->data[j][0];
|
||||
}
|
||||
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
|
||||
}
|
||||
for (int j=0; j<=selEnd.y-selStart.y; j++) {
|
||||
if (j*divider>=selEnd.y-selStart.y) {
|
||||
for (int j=0; j<=sEnd.y-sStart.y; j++) {
|
||||
if (j*divider>=sEnd.y-sStart.y) {
|
||||
if (iFine==0) {
|
||||
pat->data[j+selStart.y][0]=0;
|
||||
pat->data[j+selStart.y][1]=0;
|
||||
pat->data[j+sStart.y][0]=0;
|
||||
pat->data[j+sStart.y][1]=0;
|
||||
} else {
|
||||
pat->data[j+selStart.y][iFine+1]=-1;
|
||||
pat->data[j+sStart.y][iFine+1]=-1;
|
||||
}
|
||||
} else {
|
||||
if (iFine==0) {
|
||||
pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y][0];
|
||||
pat->data[j+sStart.y][0]=patBuffer.data[j*divider+sStart.y][0];
|
||||
}
|
||||
pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y][iFine+1];
|
||||
pat->data[j+sStart.y][iFine+1]=patBuffer.data[j*divider+sStart.y][iFine+1];
|
||||
|
||||
if (iFine==0) {
|
||||
for (int k=1; k<divider; k++) {
|
||||
if ((j*divider+k)>=selEnd.y-selStart.y) break;
|
||||
if (!(pat->data[j+selStart.y][0]==0 && pat->data[j+selStart.y][1]==0)) break;
|
||||
pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y+k][0];
|
||||
pat->data[j+selStart.y][1]=patBuffer.data[j*divider+selStart.y+k][1];
|
||||
if ((j*divider+k)>=sEnd.y-sStart.y) break;
|
||||
if (!(pat->data[j+sStart.y][0]==0 && pat->data[j+sStart.y][1]==0)) break;
|
||||
pat->data[j+sStart.y][0]=patBuffer.data[j*divider+sStart.y+k][0];
|
||||
pat->data[j+sStart.y][1]=patBuffer.data[j*divider+sStart.y+k][1];
|
||||
}
|
||||
} else {
|
||||
for (int k=1; k<divider; k++) {
|
||||
if ((j*divider+k)>=selEnd.y-selStart.y) break;
|
||||
if (pat->data[j+selStart.y][iFine+1]!=-1) break;
|
||||
pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y+k][iFine+1];
|
||||
if ((j*divider+k)>=sEnd.y-sStart.y) break;
|
||||
if (pat->data[j+sStart.y][iFine+1]!=-1) break;
|
||||
pat->data[j+sStart.y][iFine+1]=patBuffer.data[j*divider+sStart.y+k][iFine+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -894,41 +906,41 @@ void FurnaceGUI::doCollapse(int divider) {
|
|||
makeUndo(GUI_UNDO_PATTERN_COLLAPSE);
|
||||
}
|
||||
|
||||
void FurnaceGUI::doExpand(int multiplier) {
|
||||
if (multiplier<1) return;
|
||||
void FurnaceGUI::doExpand(int multiplier, const SelectionPoint& sStart, const SelectionPoint& sEnd) {
|
||||
if (multiplier<2) return;
|
||||
|
||||
finishSelection();
|
||||
prepareUndo(GUI_UNDO_PATTERN_EXPAND);
|
||||
|
||||
DivPattern patBuffer;
|
||||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
int iCoarse=sStart.xCoarse;
|
||||
int iFine=sStart.xFine;
|
||||
for (; iCoarse<=sEnd.xCoarse; iCoarse++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
|
||||
maskOut(opMaskCollapseExpand,iFine);
|
||||
for (int j=selStart.y; j<=selEnd.y; j++) {
|
||||
for (int j=sStart.y; j<=sEnd.y; j++) {
|
||||
if (iFine==0) {
|
||||
patBuffer.data[j][0]=pat->data[j][0];
|
||||
}
|
||||
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
|
||||
}
|
||||
for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) {
|
||||
if ((j+selStart.y)>=e->curSubSong->patLen) break;
|
||||
for (int j=0; j<=(sEnd.y-sStart.y)*multiplier; j++) {
|
||||
if ((j+sStart.y)>=e->curSubSong->patLen) break;
|
||||
if ((j%multiplier)!=0) {
|
||||
if (iFine==0) {
|
||||
pat->data[j+selStart.y][0]=0;
|
||||
pat->data[j+selStart.y][1]=0;
|
||||
pat->data[j+sStart.y][0]=0;
|
||||
pat->data[j+sStart.y][1]=0;
|
||||
} else {
|
||||
pat->data[j+selStart.y][iFine+1]=-1;
|
||||
pat->data[j+sStart.y][iFine+1]=-1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (iFine==0) {
|
||||
pat->data[j+selStart.y][0]=patBuffer.data[j/multiplier+selStart.y][0];
|
||||
pat->data[j+sStart.y][0]=patBuffer.data[j/multiplier+sStart.y][0];
|
||||
}
|
||||
pat->data[j+selStart.y][iFine+1]=patBuffer.data[j/multiplier+selStart.y][iFine+1];
|
||||
pat->data[j+sStart.y][iFine+1]=patBuffer.data[j/multiplier+sStart.y][iFine+1];
|
||||
}
|
||||
}
|
||||
iFine=0;
|
||||
|
@ -937,6 +949,162 @@ void FurnaceGUI::doExpand(int multiplier) {
|
|||
makeUndo(GUI_UNDO_PATTERN_EXPAND);
|
||||
}
|
||||
|
||||
void FurnaceGUI::doCollapseSong(int divider) {
|
||||
if (divider<2) return;
|
||||
finishSelection();
|
||||
|
||||
UndoStep us;
|
||||
us.type=GUI_UNDO_PATTERN_COLLAPSE_SONG;
|
||||
|
||||
DivPattern patCopy;
|
||||
|
||||
size_t subSong=e->getCurrentSubSong();
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (e->curPat[i].data[j]==NULL) continue;
|
||||
|
||||
DivPattern* pat=e->curPat[i].getPattern(j,true);
|
||||
pat->copyOn(&patCopy);
|
||||
pat->clear();
|
||||
for (int k=0; k<DIV_MAX_ROWS; k++) {
|
||||
for (int l=0; l<DIV_MAX_COLS; l++) {
|
||||
if (l==0) {
|
||||
if (!(pat->data[k/divider][0]==0 && pat->data[k/divider][1]==0)) continue;
|
||||
} else {
|
||||
if (pat->data[k/divider][l+1]!=-1) continue;
|
||||
}
|
||||
|
||||
if (l==0) {
|
||||
pat->data[k/divider][l]=patCopy.data[k][l];
|
||||
}
|
||||
pat->data[k/divider][l+1]=patCopy.data[k][l+1];
|
||||
|
||||
if (l>3 && !(l&1)) { // scale effects as needed
|
||||
switch (pat->data[k/divider][l]) {
|
||||
case 0x0d:
|
||||
pat->data[k/divider][l+1]/=divider;
|
||||
break;
|
||||
case 0x0f:
|
||||
pat->data[k/divider][l+1]=CLAMP(pat->data[k/divider][l+1]*divider,1,255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// put undo
|
||||
for (int k=0; k<DIV_MAX_ROWS; k++) {
|
||||
for (int l=0; l<DIV_MAX_COLS; l++) {
|
||||
if (pat->data[k][l]!=patCopy.data[k][l]) {
|
||||
us.pat.push_back(UndoPatternData(subSong,i,j,k,l,patCopy.data[k][l],pat->data[k][l]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// magic
|
||||
unsigned char* subSongInfoCopy=new unsigned char[1024];
|
||||
memcpy(subSongInfoCopy,e->curSubSong,1024);
|
||||
e->curSubSong->patLen/=divider;
|
||||
for (int i=0; i<e->curSubSong->speeds.len; i++) {
|
||||
e->curSubSong->speeds.val[i]=CLAMP(e->curSubSong->speeds.val[i]*divider,1,255);
|
||||
}
|
||||
unsigned char* newSubSongInfo=(unsigned char*)e->curSubSong;
|
||||
for (int i=0; i<1024; i++) {
|
||||
if (subSongInfoCopy[i]!=newSubSongInfo[i]) {
|
||||
us.other.push_back(UndoOtherData(GUI_UNDO_TARGET_SUBSONG,subSong,i,subSongInfoCopy[i],newSubSongInfo[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (!us.pat.empty()) {
|
||||
undoHist.push_back(us);
|
||||
redoHist.clear();
|
||||
if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front();
|
||||
}
|
||||
|
||||
if (e->isPlaying()) e->play();
|
||||
}
|
||||
|
||||
void FurnaceGUI::doExpandSong(int multiplier) {
|
||||
if (multiplier<2) return;
|
||||
if (e->curSubSong->patLen>(256/multiplier)) {
|
||||
showError("can't expand any further!");
|
||||
return;
|
||||
}
|
||||
finishSelection();
|
||||
|
||||
UndoStep us;
|
||||
us.type=GUI_UNDO_PATTERN_EXPAND_SONG;
|
||||
|
||||
DivPattern patCopy;
|
||||
|
||||
size_t subSong=e->getCurrentSubSong();
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (e->curPat[i].data[j]==NULL) continue;
|
||||
|
||||
DivPattern* pat=e->curPat[i].getPattern(j,true);
|
||||
pat->copyOn(&patCopy);
|
||||
pat->clear();
|
||||
for (int k=0; k<(256/multiplier); k++) {
|
||||
for (int l=0; l<DIV_MAX_COLS; l++) {
|
||||
if (l==0) {
|
||||
if (!(pat->data[k*multiplier][0]==0 && pat->data[k*multiplier][1]==0)) continue;
|
||||
} else {
|
||||
if (pat->data[k*multiplier][l+1]!=-1) continue;
|
||||
}
|
||||
|
||||
if (l==0) {
|
||||
pat->data[k*multiplier][l]=patCopy.data[k][l];
|
||||
}
|
||||
pat->data[k*multiplier][l+1]=patCopy.data[k][l+1];
|
||||
|
||||
if (l>3 && !(l&1)) { // scale effects as needed
|
||||
switch (pat->data[k*multiplier][l]) {
|
||||
case 0x0d:
|
||||
pat->data[k*multiplier][l+1]/=multiplier;
|
||||
break;
|
||||
case 0x0f:
|
||||
pat->data[k*multiplier][l+1]=CLAMP(pat->data[k*multiplier][l+1]/multiplier,1,255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// put undo
|
||||
for (int k=0; k<DIV_MAX_ROWS; k++) {
|
||||
for (int l=0; l<DIV_MAX_COLS; l++) {
|
||||
if (pat->data[k][l]!=patCopy.data[k][l]) {
|
||||
us.pat.push_back(UndoPatternData(subSong,i,j,k,l,patCopy.data[k][l],pat->data[k][l]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// magic
|
||||
unsigned char* subSongInfoCopy=new unsigned char[1024];
|
||||
memcpy(subSongInfoCopy,e->curSubSong,1024);
|
||||
e->curSubSong->patLen*=multiplier;
|
||||
for (int i=0; i<e->curSubSong->speeds.len; i++) {
|
||||
e->curSubSong->speeds.val[i]=CLAMP(e->curSubSong->speeds.val[i]/multiplier,1,255);
|
||||
}
|
||||
unsigned char* newSubSongInfo=(unsigned char*)e->curSubSong;
|
||||
for (int i=0; i<1024; i++) {
|
||||
if (subSongInfoCopy[i]!=newSubSongInfo[i]) {
|
||||
us.other.push_back(UndoOtherData(GUI_UNDO_TARGET_SUBSONG,subSong,i,subSongInfoCopy[i],newSubSongInfo[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (!us.pat.empty()) {
|
||||
undoHist.push_back(us);
|
||||
redoHist.clear();
|
||||
if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front();
|
||||
}
|
||||
|
||||
if (e->isPlaying()) e->play();
|
||||
}
|
||||
|
||||
void FurnaceGUI::doDrag() {
|
||||
int len=dragEnd.xCoarse-dragStart.xCoarse+1;
|
||||
|
||||
|
@ -985,6 +1153,8 @@ void FurnaceGUI::doUndo() {
|
|||
case GUI_UNDO_PATTERN_FLIP:
|
||||
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||
case GUI_UNDO_PATTERN_EXPAND:
|
||||
case GUI_UNDO_PATTERN_COLLAPSE_SONG:
|
||||
case GUI_UNDO_PATTERN_EXPAND_SONG:
|
||||
case GUI_UNDO_PATTERN_DRAG:
|
||||
case GUI_UNDO_REPLACE:
|
||||
for (UndoPatternData& i: us.pat) {
|
||||
|
@ -1005,6 +1175,22 @@ void FurnaceGUI::doUndo() {
|
|||
break;
|
||||
}
|
||||
|
||||
bool shallReplay=false;
|
||||
for (UndoOtherData& i: us.other) {
|
||||
switch (i.target) {
|
||||
case GUI_UNDO_TARGET_SONG:
|
||||
((unsigned char*)(&e->song))[i.off]=i.oldVal;
|
||||
shallReplay=true;
|
||||
break;
|
||||
case GUI_UNDO_TARGET_SUBSONG:
|
||||
if (i.subtarget<0 || i.subtarget>=(int)e->song.subsong.size()) break;
|
||||
((unsigned char*)(e->song.subsong[i.subtarget]))[i.off]=i.oldVal;
|
||||
shallReplay=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shallReplay && e->isPlaying()) play();
|
||||
|
||||
if (curOrder>=e->curSubSong->ordersLen) {
|
||||
curOrder=e->curSubSong->ordersLen-1;
|
||||
oldOrder=curOrder;
|
||||
|
@ -1045,6 +1231,8 @@ void FurnaceGUI::doRedo() {
|
|||
case GUI_UNDO_PATTERN_COLLAPSE:
|
||||
case GUI_UNDO_PATTERN_EXPAND:
|
||||
case GUI_UNDO_PATTERN_DRAG:
|
||||
case GUI_UNDO_PATTERN_COLLAPSE_SONG:
|
||||
case GUI_UNDO_PATTERN_EXPAND_SONG:
|
||||
case GUI_UNDO_REPLACE:
|
||||
for (UndoPatternData& i: us.pat) {
|
||||
e->changeSongP(i.subSong);
|
||||
|
@ -1065,6 +1253,22 @@ void FurnaceGUI::doRedo() {
|
|||
break;
|
||||
}
|
||||
|
||||
bool shallReplay=false;
|
||||
for (UndoOtherData& i: us.other) {
|
||||
switch (i.target) {
|
||||
case GUI_UNDO_TARGET_SONG:
|
||||
((unsigned char*)(&e->song))[i.off]=i.newVal;
|
||||
shallReplay=true;
|
||||
break;
|
||||
case GUI_UNDO_TARGET_SUBSONG:
|
||||
if (i.subtarget<0 || i.subtarget>=(int)e->song.subsong.size()) break;
|
||||
((unsigned char*)(e->song.subsong[i.subtarget]))[i.off]=i.newVal;
|
||||
shallReplay=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shallReplay && e->isPlaying()) play();
|
||||
|
||||
if (curOrder>=e->curSubSong->ordersLen) {
|
||||
curOrder=e->curSubSong->ordersLen-1;
|
||||
oldOrder=curOrder;
|
||||
|
|
|
@ -2842,9 +2842,23 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
|||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("flip selection",BIND_FOR(GUI_ACTION_PAT_FLIP_SELECTION))) doFlip();
|
||||
if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(2);
|
||||
if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(2);
|
||||
if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(2,selStart,selEnd);
|
||||
if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(2,selStart,selEnd);
|
||||
|
||||
if (topMenu) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("collapse pattern",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_PAT))) doAction(GUI_ACTION_PAT_COLLAPSE_PAT);
|
||||
if (ImGui::MenuItem("expand pattern",BIND_FOR(GUI_ACTION_PAT_EXPAND_PAT))) doAction(GUI_ACTION_PAT_EXPAND_PAT);
|
||||
}
|
||||
}
|
||||
|
||||
if (topMenu) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("collapse song",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_SONG))) doAction(GUI_ACTION_PAT_COLLAPSE_SONG);
|
||||
if (ImGui::MenuItem("expand song",BIND_FOR(GUI_ACTION_PAT_EXPAND_SONG))) doAction(GUI_ACTION_PAT_EXPAND_SONG);
|
||||
}
|
||||
|
||||
if (!basicMode) {
|
||||
if (topMenu) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("find/replace",BIND_FOR(GUI_ACTION_WINDOW_FIND),findOpen)) {
|
||||
|
@ -2856,16 +2870,6 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if (topMenu) {
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("collapse pattern",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_PAT));
|
||||
ImGui::MenuItem("expand pattern",BIND_FOR(GUI_ACTION_PAT_EXPAND_PAT));
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("collapse song",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_SONG));
|
||||
ImGui::MenuItem("expand song",BIND_FOR(GUI_ACTION_PAT_EXPAND_SONG));
|
||||
}*/
|
||||
}
|
||||
|
||||
void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
|
||||
|
|
|
@ -721,6 +721,8 @@ enum NoteCtrl {
|
|||
struct SelectionPoint {
|
||||
int xCoarse, xFine;
|
||||
int y;
|
||||
SelectionPoint(int xc, int xf, int yp):
|
||||
xCoarse(xc), xFine(xf), y(yp) {}
|
||||
SelectionPoint():
|
||||
xCoarse(0), xFine(0), y(0) {}
|
||||
};
|
||||
|
@ -742,10 +744,17 @@ enum ActionType {
|
|||
GUI_UNDO_PATTERN_FLIP,
|
||||
GUI_UNDO_PATTERN_COLLAPSE,
|
||||
GUI_UNDO_PATTERN_EXPAND,
|
||||
GUI_UNDO_PATTERN_COLLAPSE_SONG,
|
||||
GUI_UNDO_PATTERN_EXPAND_SONG,
|
||||
GUI_UNDO_PATTERN_DRAG,
|
||||
GUI_UNDO_REPLACE
|
||||
};
|
||||
|
||||
enum UndoOtherTarget {
|
||||
GUI_UNDO_TARGET_SONG,
|
||||
GUI_UNDO_TARGET_SUBSONG
|
||||
};
|
||||
|
||||
struct UndoPatternData {
|
||||
int subSong, chan, pat, row, col;
|
||||
short oldVal, newVal;
|
||||
|
@ -770,6 +779,19 @@ struct UndoOrderData {
|
|||
newVal(v2) {}
|
||||
};
|
||||
|
||||
struct UndoOtherData {
|
||||
UndoOtherTarget target;
|
||||
int subtarget;
|
||||
size_t off;
|
||||
unsigned char oldVal, newVal;
|
||||
UndoOtherData(UndoOtherTarget t, int st, size_t o, unsigned char v1, unsigned char v2):
|
||||
target(t),
|
||||
subtarget(st),
|
||||
off(o),
|
||||
oldVal(v1),
|
||||
newVal(v2) {}
|
||||
};
|
||||
|
||||
struct UndoStep {
|
||||
ActionType type;
|
||||
SelectionPoint cursor, selStart, selEnd;
|
||||
|
@ -779,6 +801,7 @@ struct UndoStep {
|
|||
int oldPatLen, newPatLen;
|
||||
std::vector<UndoOrderData> ord;
|
||||
std::vector<UndoPatternData> pat;
|
||||
std::vector<UndoOtherData> other;
|
||||
};
|
||||
|
||||
// -1 = any
|
||||
|
@ -2064,8 +2087,10 @@ class FurnaceGUI {
|
|||
void doScale(float top);
|
||||
void doRandomize(int bottom, int top, bool mode);
|
||||
void doFlip();
|
||||
void doCollapse(int divider);
|
||||
void doExpand(int multiplier);
|
||||
void doCollapse(int divider, const SelectionPoint& sStart, const SelectionPoint& sEnd);
|
||||
void doExpand(int multiplier, const SelectionPoint& sStart, const SelectionPoint& sEnd);
|
||||
void doCollapseSong(int divider);
|
||||
void doExpandSong(int multiplier);
|
||||
void doUndo();
|
||||
void doRedo();
|
||||
void doFind();
|
||||
|
|
Loading…
Reference in a new issue