diff --git a/src/activities/piano_composition/MultipleStaff.qml b/src/activities/piano_composition/MultipleStaff.qml --- a/src/activities/piano_composition/MultipleStaff.qml +++ b/src/activities/piano_composition/MultipleStaff.qml @@ -44,6 +44,7 @@ property alias flickableStaves: flickableStaves signal noteClicked(string noteName, string noteType, int noteIndex, int staffIndex) + signal pushToUndoStack(int noteIndex, int staffIndex, string oldNoteName, string oldNoteType) Flickable { id: flickableStaves @@ -98,6 +99,19 @@ } } + function undoChange(undoNoteDetails) { + if(undoNoteDetails.oldNoteName_ === "none") { + staves.itemAt(undoNoteDetails.staffIndex_).notes.remove(undoNoteDetails.noteIndex_) + if((staves.itemAt(undoNoteDetails.staffIndex_).notes.count <= 0) && (currentStaff != 0)) + currentStaff-- + } + else { + noteToReplace = [undoNoteDetails.noteIndex_, undoNoteDetails.staffIndex_] + replaceNote(undoNoteDetails.oldNoteName_, undoNoteDetails.oldNoteType_) + } + noteToReplace = [-1, -1] + } + function playNoteAudio(noteName, noteType) { if(noteType != "Rest") { var audioPitchType diff --git a/src/activities/piano_composition/Piano_composition.qml b/src/activities/piano_composition/Piano_composition.qml --- a/src/activities/piano_composition/Piano_composition.qml +++ b/src/activities/piano_composition/Piano_composition.qml @@ -181,6 +181,13 @@ else multipleStaff.eraseNote(noteIndex, staffIndex) } + onPushToUndoStack: { + // If we have undid the change, we won't push the undid change in the stack as the undoStack will enter in a loop. + if(Activity.undidChange) + Activity.undidChange = false + else + Activity.pushToStack(noteIndex, staffIndex, oldNoteName, oldNoteType) + } } GCButtonScroll { @@ -300,7 +307,11 @@ source: "qrc:/gcompris/src/activities/piano_composition/resource/%1.svg".arg(optionsRow.staffModes[currentIndex]) anchors.top: parent.top anchors.topMargin: 4 - onClicked: background.staffMode = optionsRow.staffModes[currentIndex] + onClicked: { + background.staffMode = optionsRow.staffModes[currentIndex] + if(background.staffMode != "replace") + multipleStaff.noteToReplace = [-1, -1] + } visible: true } @@ -314,6 +325,16 @@ } } + Image { + id: undoButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/undo.svg" + sourceSize.width: 50 + MouseArea { + anchors.fill: parent + onClicked: Activity.undoChange() + } + } + Image { id: openButton source: "qrc:/gcompris/src/activities/piano_composition/resource/open.svg" diff --git a/src/activities/piano_composition/Staff.qml b/src/activities/piano_composition/Staff.qml --- a/src/activities/piano_composition/Staff.qml +++ b/src/activities/piano_composition/Staff.qml @@ -144,11 +144,16 @@ else duration = calculateTimerDuration(noteType) - if(!isReplacing) + if(!isReplacing) { + multipleStaff.pushToUndoStack(notes.count, staffNb, "none", "none", noteName, noteType) notes.append({"noteName_": noteName, "noteType_": noteType, "mDuration": duration, "highlightWhenPlayed": highlightWhenPlayed}) - else + } + else { + var oldNoteDetails = notes.get(multipleStaff.noteToReplace[0]) + multipleStaff.pushToUndoStack(multipleStaff.noteToReplace[0], staffNb, oldNoteDetails.noteName_, oldNoteDetails.noteType_) notes.set(multipleStaff.noteToReplace[0], { "noteName_": noteName, "noteType_": noteType, "mDuration": duration }) + } } function replaceNote(newNoteName, newType) { @@ -167,7 +172,9 @@ else restName = "eighth" - notes.set(noteIndex, { "noteName_": restName, "noteType_": "Rest" }) + var oldNoteDetails = notes.get(noteIndex) + multipleStaff.pushToUndoStack(noteIndex, staffNb, oldNoteDetails.noteName_, oldNoteDetails.noteType_) + notes.set(noteIndex, { "noteName_": restName, "noteType_": "Rest" }) } function playNote(noteId) { diff --git a/src/activities/piano_composition/piano_composition.js b/src/activities/piano_composition/piano_composition.js --- a/src/activities/piano_composition/piano_composition.js +++ b/src/activities/piano_composition/piano_composition.js @@ -31,6 +31,8 @@ var notesDetails = NoteNotations.get() var userDir = "file://" + GCompris.ApplicationInfo.getSharedWritablePath() + "/" + "piano_composition" var userFile = userDir + "/melodies.json" +var undoStack = [] +var undidChange = false var instructions = [{ "text": qsTr("This is the treble cleff staff for high pitched notes") }, @@ -93,6 +95,25 @@ items.background.staffMode = "add" items.multipleStaff.noteToReplace = [-1, -1] items.staffModesOptions.currentIndex = 0 + undoStack = [] +} + +function pushToStack(noteIndex, staffIndex, oldNoteName, oldNoteType, newNoteName, newNoteType) { + undoStack.push({"noteIndex_": noteIndex, "staffIndex_": staffIndex, + "oldNoteName_": oldNoteName, "oldNoteType_": oldNoteType}) + // Maintain most recent 5 changes. Remove older ones (stack behaves as queue here). + if(undoStack.length > 5) + undoStack.shift() +} + +function undoChange() { + if(undoStack.length > 0) { + var undoNoteDetails = undoStack[undoStack.length - 1] + undoStack.pop() + if(undoNoteDetails.noteName_ != "none") + undidChange = true + items.multipleStaff.undoChange(undoNoteDetails) + } } function getNoteDetails(noteName, noteType) { diff --git a/src/activities/piano_composition/resource/undo.svg b/src/activities/piano_composition/resource/undo.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/undo.svg @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Edit Undo + + + edit + undo + revert + + + + + + + + + + + + + + + + +