1package songlist 2 3import ( 4 "github.com/ambientsound/pms/song" 5 "github.com/ambientsound/pms/utils" 6) 7 8type Column struct { 9 tag string 10 items int 11 totalWidth int 12 maxWidth int 13 avg int 14 width int 15} 16 17type Columns []*Column 18 19type ColumnMap map[string]*Column 20 21// NewColumn returns a new Column. 22func NewColumn(tag string) *Column { 23 return &Column{tag: tag} 24} 25 26// Set calculates all song's widths. 27func (c *Column) Set(s Songlist) { 28 c.Reset() 29 for _, song := range s.Songs() { 30 c.Add(song) 31 } 32} 33 34// Add a single song's width to the total and maximum width. 35func (c *Column) Add(song *song.Song) { 36 l := len(song.Tags[c.tag]) 37 if l == 0 { 38 return 39 } 40 c.avg = 0 41 c.items++ 42 c.totalWidth += l 43 c.maxWidth = utils.Max(c.maxWidth, l) 44} 45 46// Remove a single song's tag width from the total and maximum width. 47func (c *Column) Remove(song *song.Song) { 48 l := len(song.Tags[c.tag]) 49 if l == 0 { 50 return 51 } 52 c.avg = 0 53 c.items-- 54 c.totalWidth -= l 55 // TODO: c.maxWidth is not updated 56} 57 58// Reset sets all values to zero. 59func (c *Column) Reset() { 60 c.items = 0 61 c.totalWidth = 0 62 c.maxWidth = 0 63 c.avg = 0 64 c.width = 0 65} 66 67// Weight returns the relative usefulness of this column. It might happen that 68// a tag appears rarely, but is very long. In this case we reduce the field so 69// that other tags get more space. 70func (c *Column) Weight(max int) float64 { 71 return float64(c.items) / float64(max) 72} 73 74// Avg returns the average length of the tag values in this column. 75func (c *Column) Avg() int { 76 if c.avg == 0 { 77 if c.items == 0 { 78 c.avg = 0 79 } else { 80 c.avg = c.totalWidth / c.items 81 } 82 } 83 //console.Log("Avg() of %s is %d", c.Tag(), c.avg) 84 return c.avg 85} 86 87// Tag returns the tag name. 88func (c *Column) Tag() string { 89 return c.tag 90} 91 92// MaxWidth returns the length of the longest tag value in this column. 93func (c *Column) MaxWidth() int { 94 return c.maxWidth 95} 96 97// Width returns the column width. 98func (c *Column) Width() int { 99 return c.width 100} 101 102// SetWidth sets the width that the column should consume. 103func (c *Column) SetWidth(width int) { 104 c.width = width 105} 106 107// expand adjusts the column widths equally between the different columns, 108// giving affinity to weight. 109func (columns Columns) Expand(totalWidth int) { 110 if len(columns) == 0 { 111 return 112 } 113 114 usedWidth := 0 115 poolSize := len(columns) 116 saturated := make([]bool, poolSize) 117 118 // Start with the average value 119 for i := range columns { 120 avg := columns[i].Avg() 121 columns[i].SetWidth(avg) 122 usedWidth += avg 123 } 124 125 // expand as long as there is space left 126 for { 127 for i := range columns { 128 if usedWidth > totalWidth { 129 return 130 } 131 if poolSize > 0 && saturated[i] { 132 continue 133 } 134 col := columns[i] 135 if poolSize > 0 && col.Width() > col.MaxWidth() { 136 saturated[i] = true 137 poolSize-- 138 continue 139 } 140 col.SetWidth(col.Width() + 1) 141 usedWidth++ 142 } 143 } 144} 145 146// Add adds song tags to all applicable columns. 147func (c ColumnMap) Add(song *song.Song) { 148 for tag := range song.StringTags { 149 c[tag].Add(song) 150 } 151} 152 153// Remove removes song tags from all applicable columns. 154func (c ColumnMap) Remove(song *song.Song) { 155 for tag := range song.StringTags { 156 c[tag].Remove(song) 157 } 158} 159 160// ensureColumns makes sure that all of a song's tags exists in the column map. 161func (s *BaseSonglist) ensureColumns(song *song.Song) { 162 for tag := range song.StringTags { 163 if _, ok := s.columns[tag]; !ok { 164 s.columns[tag] = NewColumn(tag) 165 } 166 } 167} 168 169// Columns returns a slice of columns, containing only the columns which has 170// the specified tags. 171func (s *BaseSonglist) Columns(columns []string) Columns { 172 cols := make(Columns, 0) 173 for _, tag := range columns { 174 col := s.columns[tag] 175 if col == nil { 176 col = NewColumn(tag) 177 } 178 cols = append(cols, col) 179 } 180 return cols 181} 182