#include "CMacroRecorder.h" #include "settings.h" #include "AStatusComm.h" #include "CActionParameters.h" #include "AFrontendHooks.h" #include "CLoadedSound.h" #include "CSoundPlayerChannel.h" #include "CSound.h" #include "AAction.h" #include "ASoundClipboard.h" #include CMacroRecorder::CMacroRecorder() : recording(false), file(NULL) { } CMacroRecorder::~CMacroRecorder() { // would like to handle an unstopped recording, but it's possible that the CNestedDataFile we know about has been closed.. } bool CMacroRecorder::isRecording() const { return recording; } void CMacroRecorder::startRecording(CNestedDataFile *_file,const string _macroName) { if(recording) throw runtime_error(string(__func__)+" -- already recording"); macroName=_macroName; file=_file; key=macroName; if(file->keyExists(key)) { if(Question(_("A macro already exists with the name: ")+macroName+"\n\n"+_("Do you want to overwrite it?"),yesnoQues)==yesAns) removeMacro(file,macroName); else return; } actionCount=0; activeSoundIndex=-1; // indicates to use the active sound file->setValue(key DOT "actionCount",actionCount); recording=true; } void CMacroRecorder::stopRecording() { if(recording) { recording=false; // finish up if(actionCount<=0) { Message(_("Nothing was recorded in the macro: ")+macroName); file->removeKey(key); } else { // now add the macro name to the selectable list of macro's to play vector macroNames=file->getValue >("MacroNames"); macroNames.push_back(macroName); // sort the macro names sort(macroNames.begin(),macroNames.end()); // remove duplicates unique(macroNames.begin(),macroNames.end()); // write new macro name list to file file->setValue >("MacroNames",macroNames); } file->save(); } } bool CMacroRecorder::pushAction(const string actionName,const CActionParameters *actionParameters,CLoadedSound *loadedSound) { if(!recording) throw runtime_error(string(__func__)+" -- not recording"); if(gRegisteredActionFactories.find(actionName)==gRegisteredActionFactories.end()) { Message(_("The action just performed will not be recorded in your macro.")+string("\n\n")+actionName); return true; } const AActionFactory *actionFactory=gRegisteredActionFactories[actionName]; // ask the user how they want to handle certain things for this action when the macro is played back AFrontendHooks::MacroActionParameters macroActionParameters; if(!gFrontendHooks->showMacroActionParamsDialog(actionFactory,macroActionParameters,loadedSound)) return false; const string actionKey=key DOT "action"+istring(actionCount,3,true); file->setValue(actionKey DOT "actionName",actionName); if(activeSoundIndex>=0) file->setValue(actionKey DOT "activeSoundIndex",(size_t)activeSoundIndex); activeSoundIndex=-1; // and from now out use activeSound (until next pushActiveSoundChange) call file->setValue(actionKey DOT "askToPromptForActionParametersAtPlayback",macroActionParameters.askToPromptForActionParametersAtPlayback); file->setValue(actionKey DOT "selectedClipboardDescription",AAction::clipboards[gWhichClipboard]->getDescription()); file->setValue(actionKey DOT "selectionPositionsAreApplicable",actionFactory->selectionPositionsAreApplicable); if(loadedSound && actionFactory->selectionPositionsAreApplicable) { // store info for positioning the start and stop positions at playback file->setValue(actionKey DOT "positioning" DOT "startPosition",loadedSound->channel->getStartPosition()); file->setValue(actionKey DOT "positioning" DOT "stopPosition",loadedSound->channel->getStopPosition()); file->setValue(actionKey DOT "positioning" DOT "audioLength",loadedSound->sound->getLength()); file->setValue(actionKey DOT "positioning" DOT "startPosPositioning",macroActionParameters.startPosPositioning); file->setValue(actionKey DOT "positioning" DOT "stopPosPositioning",macroActionParameters.stopPosPositioning); file->setValue(actionKey DOT "positioning" DOT "startPosCueName",macroActionParameters.startPosCueName); file->setValue(actionKey DOT "positioning" DOT "stopPosCueName",macroActionParameters.stopPosCueName); // store info about crossfade file->setValue(actionKey DOT "positioning" DOT "crossfadeEdges",gCrossfadeEdges); file->setValue(actionKey DOT "positioning" DOT "crossfadeStartTime",gCrossfadeStartTime); file->setValue(actionKey DOT "positioning" DOT "crossfadeStopTime",gCrossfadeStopTime); file->setValue(actionKey DOT "positioning" DOT "crossfadeFadeMethod",gCrossfadeFadeMethod); } actionParameters->writeToFile(file,actionKey DOT "parameters"); file->setValue(key DOT "actionCount",++actionCount); return true; } void CMacroRecorder::pushActiveSoundChange(size_t index) { activeSoundIndex=(int)index; } void CMacroRecorder::popAction(const string actionName) { if(!recording) throw runtime_error(string(__func__)+" -- not recording"); if(gRegisteredActionFactories.find(actionName)==gRegisteredActionFactories.end()) return; // wouldn't have been recorded if(actionCount>0) { const string actionKey=key DOT "action"+istring(actionCount-1,3,true); if(file->getValue(actionKey DOT "actionName")==actionName) { // forget the last action we recorded actionCount--; file->removeKey(actionKey); file->setValue(key DOT "actionCount",actionCount); } // else otherwise, we didn't add this action for whatever reason (probably wasn't in the action registry) } } void CMacroRecorder::removeMacro(CNestedDataFile *file,const string macroName) { // get the current list of macro names vector macroNames=file->getValue >("MacroNames"); // remove the name from the list for(vector::iterator i=macroNames.begin();i!=macroNames.end();i++) { if((*i)==macroName) { i=macroNames.erase(i); if(i==macroNames.end()) // still not quite sure about the best way to iterate over a vector while removing items break; } } // write new macro name list to file file->setValue >("MacroNames",macroNames); // now remove the macro definition from the file (??? if I could rename keys, then we could always write to a temporary name and name it for real afterwards) file->removeKey(macroName); }