// replace or paste items sub-chunk _tmpltItems // _paste==false for replace, paste otherwise // _p: optional (to factorize chunk updates) bool ReplacePasteItemsFromTrackTemplate(MediaTrack* _tr, WDL_FastString* _tmpltItems, bool _paste, SNM_ChunkParserPatcher* _p) { bool updated = false; if (_tr && _tr != GetMasterTrack(NULL) && _tmpltItems) // no items on master { SNM_ChunkParserPatcher* p = (_p ? _p : new SNM_ChunkParserPatcher(_tr)); // delete current items? if (!_paste) updated |= p->RemoveSubChunk("ITEM", 2, -1); // insert template items if (_tmpltItems->GetLength()) { WDL_FastString tmpltItems(_tmpltItems); // do not alter _tmpltItems! // offset items if needed int* offsOpt = (int*)GetConfigVar("templateditcursor"); // >= REAPER v4.15 if (offsOpt && *offsOpt) { double add = GetCursorPositionEx(NULL); SNM_ChunkParserPatcher pitems(&tmpltItems); pitems.ParsePatch(SNM_D_ADD, 1, "ITEM", "POSITION", -1, 1, &add); } p->GetChunk()->Insert(tmpltItems.Get(), p->GetChunk()->GetLength()-2); // -2: before ">\n" p->IncUpdates(); // as we directly work on the chunk updated = true; } if (!_p) DELETE_NULL(p); // + auto-commit if needed } return updated; }
void FSlomoTrackEditor::HandleAddSlomoTrackMenuEntryExecute() { UMovieSceneSequence* FocusedSequence = GetSequencer()->GetFocusedMovieSceneSequence(); UMovieScene* MovieScene = FocusedSequence->GetMovieScene(); if (MovieScene == nullptr) { return; } UMovieSceneTrack* SlomoTrack = MovieScene->FindMasterTrack( UMovieSceneSlomoTrack::StaticClass() ); if (SlomoTrack != nullptr) { return; } const FScopedTransaction Transaction(NSLOCTEXT("Sequencer", "AddSlomoTrack_Transaction", "Add Play Rate Track")); MovieScene->Modify(); SlomoTrack = GetMasterTrack( UMovieSceneSlomoTrack::StaticClass() ); ensure(SlomoTrack); SlomoTrack->AddSection(SlomoTrack->CreateNewSection()); GetSequencer()->NotifyMovieSceneDataChanged(); }
// return false if no track icon has been found // note: _fnOutSz is ignored atm bool GetTrackIcon(MediaTrack* _tr, char* _fnOut, int _fnOutSz) { if (_tr && _fnOut && _tr!=GetMasterTrack(NULL)) // exclude master (icon not supported yet, v4.13) { SNM_ChunkParserPatcher p(_tr); p.SetWantsMinimalState(true); return (p.Parse(SNM_GET_CHUNK_CHAR, 1, "TRACK", "TRACKIMGFN", 0, 1, _fnOut, NULL, "TRACKID") > 0); } return false; }
// take the master into account (contrary to CountSelectedTracks()) int SNM_CountSelectedTracks(ReaProject* _proj, bool _master) { int selCnt = CountSelectedTracks(_proj); if (_master) { MediaTrack* mtr = GetMasterTrack(_proj); if (mtr && *(int*)GetSetMediaTrackInfo(mtr, "I_SELECTED", NULL)) selCnt++; } return selCnt; }
// overrides the native GetTrackGUID(): returns a special GUID for the master track // (useful for persistence as no GUID is stored in RPP files for the master..) // note: TrackToGuid(NULL) will return also NULL, contrary to GetTrackGUID(NULL) const GUID* TrackToGuid(MediaTrack* tr) { if (tr) { if (tr == GetMasterTrack(NULL)) return &GUID_NULL; else return GetTrackGUID(tr); } return NULL; }
bool TrackMatchesGuid(MediaTrack* tr, const GUID* g) { if (tr && g) { if (GuidsEqual(GetTrackGUID(tr), g)) return true; // 2nd try for the master if (tr == GetMasterTrack(NULL)) return GuidsEqual(g, &GUID_NULL); } return false; }
// apply a track template (primitive: no undo, folder states are lost, receives are removed) // _tmplt: the track template // _p: optional (to factorize chunk updates) // note: assumes _tmplt contains a single track (with items/envs already removed when _itemsFromTmplt/_envsFromTmplt are false) // i.e. use MakeSingleTrackTemplateChunk() first! bool ApplyTrackTemplatePrimitive(MediaTrack* _tr, WDL_FastString* _tmplt, bool _itemsFromTmplt, bool _envsFromTmplt, SNM_SendPatcher* _p) { bool updated = false; if (_tr && _tmplt) { WDL_FastString tmplt(_tmplt); // not to mod the input template.. SNM_ChunkParserPatcher* p = (_p ? _p : new SNM_ChunkParserPatcher(_tr)); // add current track items, if any if (!_itemsFromTmplt) { /*JFB!! works with SNM_ChunkParserPatcher v2 WDL_FastString curItems; if (p->GetSubChunk("ITEM", 2, -1, &curItems) >= 0) // -1 to get all items in one go newChunk.Insert(&curItems, newChunk.GetLength()-2); // -2: ">\n", */ // insert current items in template int posItems = p->GetSubChunk("ITEM", 2, 0); if (posItems >= 0) tmplt.Insert((char*)(p->GetChunk()->Get()+posItems), tmplt.GetLength()-2, p->GetChunk()->GetLength()-posItems-2); // -2: ">\n" } // safety: force items removal for master track //JFB REAPER BUG: test required, items would be added to the master (!) else if (_tr == GetMasterTrack(NULL)) { SNM_ChunkParserPatcher ptmplt(&tmplt); ptmplt.RemoveSubChunk("ITEM", 2, -1); } // add current track envs in template, if any if (!_envsFromTmplt) { /*JFB simple casting ko: _mode mess, etc... if (const char* envs = ((SNM_TrackEnvParserPatcher*)p)->GetTrackEnvelopes()) */ SNM_TrackEnvParserPatcher penv(p->GetChunk(), false); // no auto-commit! if (const char* envs = penv.GetTrackEnvelopes()) // get all track envs in one go, exclude fx param envs { SNM_ChunkParserPatcher ptmplt(&tmplt); // ">ITEM": best effort for the break keyword (fx chain might not exist, etc..) ptmplt.InsertAfterBefore(1, envs, "TRACK", "MAINSEND", 1, 0, "<ITEM"); } } // the meat, apply template! p->SetChunk(tmplt.Get()); updated = true; if (!_p) DELETE_NULL(p); // + auto-commit if needed } return updated; }
// get a track from a project by selected track count index // rmk: to be used with SNM_CountSelectedTracks() and not the API's // CountSelectedTracks() which does not take the master into account.. MediaTrack* SNM_GetSelectedTrack(ReaProject* _proj, int _idx, bool _withMaster) { if (_withMaster) { MediaTrack* mtr = GetMasterTrack(_proj); if (mtr && *(int*)GetSetMediaTrackInfo(mtr, "I_SELECTED", NULL)) { if (!_idx) return mtr; else return GetSelectedTrack(_proj, _idx-1); } } return GetSelectedTrack(_proj, _idx); }
// primitive (no undo point), removes track icon if _fn == "" bool SetTrackIcon(MediaTrack* _tr, const char* _fn) { bool updated = false; if (_tr && _fn && _tr!=GetMasterTrack(NULL)) // exclude master (icon not supported yet, v4.13) { SNM_ChunkParserPatcher p(_tr); // nothing done yet int iconChunkPos = p.Parse(SNM_GET_CHUNK_CHAR, 1, "TRACK", "TRACKIMGFN", 0, 1, NULL, NULL, "TRACKID"); char pIconLine[SNM_MAX_CHUNK_LINE_LENGTH] = ""; if (_snprintfStrict(pIconLine, sizeof(pIconLine), "TRACKIMGFN \"%s\"\n", _fn) > 0) { if (iconChunkPos > 0) updated |= p.ReplaceLine(--iconChunkPos, pIconLine); //JFB!! to update with SNM_ChunkParserPatcher v2 else updated |= p.InsertAfterBefore(0, pIconLine, "TRACK", "FX", 1, 0, "TRACKID"); } } return updated; }
// apply a track template (preserves routings and folder states) // _p: optional (to factorize chunk updates) // note: assumes _tmplt contains a single track (with items/envs already removed when _itemsFromTmplt/_envsFromTmplt are false) // i.e. use MakeSingleTrackTemplateChunk() first! bool ApplyTrackTemplate(MediaTrack* _tr, WDL_FastString* _tmplt, bool _itemsFromTmplt, bool _envsFromTmplt, void* _p) { bool updated = false; if (_tr && _tmplt && _tmplt->GetLength()) { SNM_SendPatcher* p = (_p ? (SNM_SendPatcher*)_p : new SNM_SendPatcher(_tr)); // store receives, folder and compact states WDL_PtrList_DeleteOnDestroy<WDL_PtrList_DeleteOnDestroy<SNM_SndRcv> > rcvs; WDL_PtrList_DeleteOnDestroy<WDL_PtrList_DeleteOnDestroy<SNM_SndRcv> > snds; WDL_PtrList<MediaTrack> trs; trs.Add(_tr); WDL_FastString busLine, compbusLine; if (_tr != GetMasterTrack(NULL)) { CopySendsReceives(true, &trs, &snds, &rcvs); // parse twice but it does not cost much (search at the very start of the chunk) p->Parse(SNM_GET_SUBCHUNK_OR_LINE, 1, "TRACK", "ISBUS", 0, -1, &busLine, NULL, "BUSCOMP"); p->Parse(SNM_GET_SUBCHUNK_OR_LINE, 1, "TRACK", "BUSCOMP", 0, -1, &compbusLine, NULL, "SHOWINMIX"); } // apply tr template if ((updated = ApplyTrackTemplatePrimitive(_tr, _tmplt, _itemsFromTmplt, _envsFromTmplt, p))) { // restore receives, folder and compact states WDL_PtrList<SNM_ChunkParserPatcher> ps; ps.Add(p); PasteSendsReceives(&trs, &snds, &rcvs, &ps); if (busLine.GetLength()) p->ReplaceLine("TRACK", "ISBUS", 1, 0, busLine.Get(), "BUSCOMP"); if (compbusLine.GetLength()) p->ReplaceLine("TRACK", "BUSCOMP", 1, 0, compbusLine.Get(), "SHOWINMIX"); } /*JFB!! works with SNM_ChunkParserPatcher v2 + "SNM_SendPatcher" to remove // store current receives, folder and compact states // done through the chunk because the API is incomplete for folder states (tcp only) WDL_FastString rcvs, busLine, compbusLine; if (_tr != GetMasterTrack(NULL)) { // parse twice but it does not cost much (search at the very start of the chunk) p->Parse(SNM_GET_SUBCHUNK_OR_LINE, 1, "TRACK", "AUXRECV", -1, -1, &rcvs, NULL, "MIDIOUT"); p->Parse(SNM_GET_SUBCHUNK_OR_LINE, 1, "TRACK", "ISBUS", 0, -1, &busLine, NULL, "BUSCOMP"); p->Parse(SNM_GET_SUBCHUNK_OR_LINE, 1, "TRACK", "BUSCOMP", 0, -1, &compbusLine, NULL, "SHOWINMIX"); } // apply tr template if (updated = ApplyTrackTemplatePrimitive(_tr, _tmpltChunk, _itemsFromTmplt, _envsFromTmplt, p)) { // restore receives, folder and compact states if (busLine.GetLength()) p->ReplaceLine("TRACK", "ISBUS", 1, 0, busLine.Get(), "BUSCOMP"); if (compbusLine.GetLength()) p->ReplaceLine("TRACK", "BUSCOMP", 1, 0, compbusLine.Get(), "SHOWINMIX"); if (rcvs.GetLength()) p->InsertAfterBefore(0, rcvs.Get(), "TRACK", "MIDIOUT", 1, 0, "MAINSEND"); } */ if (!_p) DELETE_NULL(p); // + auto-commit if needed } return updated; }
// get a track from a project by track count (1-based, 0 for master) MediaTrack* SNM_GetTrack(ReaProject* _proj, int _idx) { if (!_idx) return GetMasterTrack(_proj); return GetTrack(_proj, _idx-1); }