mirror of
https://github.com/vsariola/sointu.git
synced 2025-06-04 01:28:45 -04:00
192 lines
5.1 KiB
Go
192 lines
5.1 KiB
Go
package tracker
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/vsariola/sointu"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
type (
|
|
UnitListItem struct {
|
|
Type, Comment string
|
|
Disabled bool
|
|
StackNeed, StackBefore, StackAfter int
|
|
}
|
|
|
|
UnitYieldFunc func(index int, item UnitListItem) (ok bool)
|
|
UnitSearchYieldFunc func(index int, item string) (ok bool)
|
|
|
|
Units Model // Units is a list of all the units in the selected instrument, implementing ListData & MutableListData interfaces
|
|
|
|
)
|
|
|
|
// Model methods
|
|
|
|
func (m *Model) Units() *Units { return (*Units)(m) }
|
|
|
|
// Units methods
|
|
|
|
func (ul *Units) List() List {
|
|
return List{ul}
|
|
}
|
|
|
|
func (ul *Units) SelectedType() string {
|
|
if ul.d.InstrIndex < 0 ||
|
|
ul.d.InstrIndex >= len(ul.d.Song.Patch) ||
|
|
ul.d.UnitIndex < 0 ||
|
|
ul.d.UnitIndex >= len(ul.d.Song.Patch[ul.d.InstrIndex].Units) {
|
|
return ""
|
|
}
|
|
return ul.d.Song.Patch[ul.d.InstrIndex].Units[ul.d.UnitIndex].Type
|
|
}
|
|
|
|
func (ul *Units) SetSelectedType(t string) {
|
|
if ul.d.InstrIndex < 0 ||
|
|
ul.d.InstrIndex >= len(ul.d.Song.Patch) {
|
|
return
|
|
}
|
|
if ul.d.UnitIndex < 0 {
|
|
ul.d.UnitIndex = 0
|
|
}
|
|
for len(ul.d.Song.Patch[ul.d.InstrIndex].Units) <= ul.d.UnitIndex {
|
|
ul.d.Song.Patch[ul.d.InstrIndex].Units = append(ul.d.Song.Patch[ul.d.InstrIndex].Units, sointu.Unit{})
|
|
}
|
|
unit, ok := defaultUnits[t]
|
|
if !ok { // if the type is invalid, we just set it to empty unit
|
|
unit = sointu.Unit{Parameters: make(map[string]int)}
|
|
} else {
|
|
unit = unit.Copy()
|
|
}
|
|
oldUnit := ul.d.Song.Patch[ul.d.InstrIndex].Units[ul.d.UnitIndex]
|
|
if oldUnit.Type == unit.Type {
|
|
return
|
|
}
|
|
defer ul.change("SetSelectedType", MajorChange)()
|
|
ul.d.Song.Patch[ul.d.InstrIndex].Units[ul.d.UnitIndex] = unit
|
|
ul.d.Song.Patch[ul.d.InstrIndex].Units[ul.d.UnitIndex].ID = oldUnit.ID // keep the ID of the replaced unit
|
|
}
|
|
|
|
func (ul *Units) Iterate(yield UnitYieldFunc) {
|
|
if ul.d.InstrIndex < 0 || ul.d.InstrIndex >= len(ul.d.Song.Patch) {
|
|
return
|
|
}
|
|
stackBefore := 0
|
|
for i, unit := range ul.d.Song.Patch[ul.d.InstrIndex].Units {
|
|
stackAfter := stackBefore + unit.StackChange()
|
|
if !yield(i, UnitListItem{
|
|
Type: unit.Type,
|
|
Comment: unit.Comment,
|
|
Disabled: unit.Disabled,
|
|
StackNeed: unit.StackNeed(),
|
|
StackBefore: stackBefore,
|
|
StackAfter: stackAfter,
|
|
}) {
|
|
break
|
|
}
|
|
stackBefore = stackAfter
|
|
}
|
|
}
|
|
|
|
func (ul *Units) Selected() int {
|
|
return max(min(ul.d.UnitIndex, ul.Count()-1), 0)
|
|
}
|
|
|
|
func (ul *Units) Selected2() int {
|
|
return max(min(ul.d.UnitIndex2, ul.Count()-1), 0)
|
|
}
|
|
|
|
func (ul *Units) SetSelected(value int) {
|
|
m := (*Model)(ul)
|
|
m.d.UnitIndex = max(min(value, ul.Count()-1), 0)
|
|
m.d.ParamIndex = 0
|
|
m.d.UnitSearching = false
|
|
m.d.UnitSearchString = ""
|
|
}
|
|
|
|
func (ul *Units) SetSelected2(value int) {
|
|
(*Model)(ul).d.UnitIndex2 = max(min(value, ul.Count()-1), 0)
|
|
}
|
|
|
|
func (ul *Units) Count() int {
|
|
m := (*Model)(ul)
|
|
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
|
return 0
|
|
}
|
|
return len(m.d.Song.Patch[(*Model)(ul).d.InstrIndex].Units)
|
|
}
|
|
|
|
func (ul *Units) move(r Range, delta int) (ok bool) {
|
|
m := (*Model)(ul)
|
|
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
|
return false
|
|
}
|
|
units := m.d.Song.Patch[m.d.InstrIndex].Units
|
|
for i, j := range r.Swaps(delta) {
|
|
units[i], units[j] = units[j], units[i]
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (ul *Units) delete(r Range) (ok bool) {
|
|
m := (*Model)(ul)
|
|
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
|
return false
|
|
}
|
|
u := m.d.Song.Patch[m.d.InstrIndex].Units
|
|
m.d.Song.Patch[m.d.InstrIndex].Units = append(u[:r.Start], u[r.End:]...)
|
|
return true
|
|
}
|
|
|
|
func (ul *Units) change(n string, severity ChangeSeverity) func() {
|
|
return (*Model)(ul).change("UnitListView."+n, PatchChange, severity)
|
|
}
|
|
|
|
func (ul *Units) cancel() {
|
|
(*Model)(ul).changeCancel = true
|
|
}
|
|
|
|
func (ul *Units) marshal(r Range) ([]byte, error) {
|
|
m := (*Model)(ul)
|
|
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
|
return nil, errors.New("UnitListView.marshal: no instruments")
|
|
}
|
|
units := m.d.Song.Patch[m.d.InstrIndex].Units[r.Start:r.End]
|
|
ret, err := yaml.Marshal(struct{ Units []sointu.Unit }{units})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("UnitListView.marshal: %v", err)
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (ul *Units) unmarshal(data []byte) (r Range, err error) {
|
|
m := (*Model)(ul)
|
|
if m.d.InstrIndex < 0 || m.d.InstrIndex >= len(m.d.Song.Patch) {
|
|
return Range{}, errors.New("UnitListView.unmarshal: no instruments")
|
|
}
|
|
var pastedUnits struct{ Units []sointu.Unit }
|
|
if err := yaml.Unmarshal(data, &pastedUnits); err != nil {
|
|
return Range{}, fmt.Errorf("UnitListView.unmarshal: %v", err)
|
|
}
|
|
if len(pastedUnits.Units) == 0 {
|
|
return Range{}, errors.New("UnitListView.unmarshal: no units")
|
|
}
|
|
m.assignUnitIDs(pastedUnits.Units)
|
|
sel := ul.Selected()
|
|
var ok bool
|
|
m.d.Song.Patch[m.d.InstrIndex].Units, ok = Insert(m.d.Song.Patch[m.d.InstrIndex].Units, sel, pastedUnits.Units...)
|
|
if !ok {
|
|
return Range{}, errors.New("UnitListView.unmarshal: insert failed")
|
|
}
|
|
return Range{sel, sel + len(pastedUnits.Units)}, nil
|
|
}
|
|
|
|
func (ul *Units) CurrentInstrumentUnitAt(index int) sointu.Unit {
|
|
units := ul.d.Song.Patch[ul.d.InstrIndex].Units
|
|
if index < 0 || index >= len(units) {
|
|
return sointu.Unit{}
|
|
}
|
|
return units[index]
|
|
}
|