col.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package xlsx
  2. // Default column width in excel
  3. const ColWidth = 9.5
  4. const Excel2006MaxRowCount = 1048576
  5. const Excel2006MaxRowIndex = Excel2006MaxRowCount - 1
  6. type Col struct {
  7. Min int
  8. Max int
  9. Hidden bool
  10. Width float64
  11. Collapsed bool
  12. OutlineLevel uint8
  13. numFmt string
  14. parsedNumFmt *parsedNumberFormat
  15. style *Style
  16. DataValidation []*xlsxCellDataValidation
  17. defaultCellType *CellType
  18. }
  19. // SetType will set the format string of a column based on the type that you want to set it to.
  20. // This function does not really make a lot of sense.
  21. func (c *Col) SetType(cellType CellType) {
  22. switch cellType {
  23. case CellTypeString:
  24. c.numFmt = builtInNumFmt[builtInNumFmtIndex_STRING]
  25. case CellTypeNumeric:
  26. c.numFmt = builtInNumFmt[builtInNumFmtIndex_INT]
  27. case CellTypeBool:
  28. c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL] //TEMP
  29. case CellTypeInline:
  30. c.numFmt = builtInNumFmt[builtInNumFmtIndex_STRING]
  31. case CellTypeError:
  32. c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL] //TEMP
  33. case CellTypeDate:
  34. // Cells that are stored as dates are not properly supported in this library.
  35. // They should instead be stored as a Numeric with a date format.
  36. c.numFmt = builtInNumFmt[builtInNumFmtIndex_GENERAL]
  37. case CellTypeStringFormula:
  38. c.numFmt = builtInNumFmt[builtInNumFmtIndex_STRING]
  39. }
  40. }
  41. // SetCellMetadata sets the CellMetadata related attributes
  42. // of a Col
  43. func (c *Col) SetCellMetadata(cellMetadata CellMetadata) {
  44. c.defaultCellType = &cellMetadata.cellType
  45. c.SetStreamStyle(cellMetadata.streamStyle)
  46. }
  47. // GetStyle returns the Style associated with a Col
  48. func (c *Col) GetStyle() *Style {
  49. return c.style
  50. }
  51. // SetStyle sets the style of a Col
  52. func (c *Col) SetStyle(style *Style) {
  53. c.style = style
  54. }
  55. // SetDataValidation set data validation with zero based start and end.
  56. // Set end to -1 for all rows.
  57. func (c *Col) SetDataValidation(dd *xlsxCellDataValidation, start, end int) {
  58. if end < 0 {
  59. end = Excel2006MaxRowIndex
  60. }
  61. dd.minRow = start
  62. dd.maxRow = end
  63. tmpDD := make([]*xlsxCellDataValidation, 0)
  64. for _, item := range c.DataValidation {
  65. if item.maxRow < dd.minRow {
  66. tmpDD = append(tmpDD, item) //No intersection
  67. } else if item.minRow > dd.maxRow {
  68. tmpDD = append(tmpDD, item) //No intersection
  69. } else if dd.minRow <= item.minRow && dd.maxRow >= item.maxRow {
  70. continue //union , item can be ignored
  71. } else if dd.minRow >= item.minRow {
  72. //Split into three or two, Newly added object, intersect with the current object in the lower half
  73. tmpSplit := new(xlsxCellDataValidation)
  74. *tmpSplit = *item
  75. if dd.minRow > item.minRow { //header whetherneed to split
  76. item.maxRow = dd.minRow - 1
  77. tmpDD = append(tmpDD, item)
  78. }
  79. if dd.maxRow < tmpSplit.maxRow { //footer whetherneed to split
  80. tmpSplit.minRow = dd.maxRow + 1
  81. tmpDD = append(tmpDD, tmpSplit)
  82. }
  83. } else {
  84. item.minRow = dd.maxRow + 1
  85. tmpDD = append(tmpDD, item)
  86. }
  87. }
  88. tmpDD = append(tmpDD, dd)
  89. c.DataValidation = tmpDD
  90. }
  91. // SetDataValidationWithStart set data validation with a zero basd start row.
  92. // This will apply to the rest of the rest of the column.
  93. func (c *Col) SetDataValidationWithStart(dd *xlsxCellDataValidation, start int) {
  94. c.SetDataValidation(dd, start, -1)
  95. }
  96. // SetStreamStyle sets the style and number format id to the ones specified in the given StreamStyle
  97. func (c *Col) SetStreamStyle(style StreamStyle) {
  98. c.style = style.style
  99. // TODO: `style.xNumFmtId` could be out of the range of the builtin map
  100. // returning "" which may not be a valid formatCode
  101. c.numFmt = builtInNumFmt[style.xNumFmtId]
  102. }
  103. func (c *Col) GetStreamStyle() StreamStyle {
  104. // TODO: Like `SetStreamStyle`, `numFmt` could be out of the range of the builtin inv map
  105. // returning 0 which maps to formatCode "general"
  106. return StreamStyle{builtInNumFmtInv[c.numFmt], c.style}
  107. }
  108. // copyToRange is an internal convenience function to make a copy of a
  109. // Col with a different Min and Max value, it is not intended as a
  110. // general purpose Col copying function as you must still insert the
  111. // resulting Col into the ColStore.
  112. func (c *Col) copyToRange(min, max int) *Col {
  113. return &Col{
  114. Min: min,
  115. Max: max,
  116. Hidden: c.Hidden,
  117. Width: c.Width,
  118. Collapsed: c.Collapsed,
  119. OutlineLevel: c.OutlineLevel,
  120. numFmt: c.numFmt,
  121. parsedNumFmt: c.parsedNumFmt,
  122. style: c.style,
  123. DataValidation: append([]*xlsxCellDataValidation{}, c.DataValidation...),
  124. defaultCellType: c.defaultCellType,
  125. }
  126. }
  127. type colStoreNode struct {
  128. Col *Col
  129. Prev *colStoreNode
  130. Next *colStoreNode
  131. }
  132. // ColStore is the working store of Col definitions, it will simplify all Cols added to it, to ensure there ar no overlapping definitions.
  133. type ColStore struct {
  134. Root *colStoreNode
  135. }
  136. // Add a Col to the ColStore. If it overwrites all, or part of some
  137. // existing Col's range of columns the that Col will be adjusted
  138. // and/or split to make room for the new Col.
  139. func (cs *ColStore) Add(col *Col) {
  140. newNode := &colStoreNode{Col: col}
  141. if cs.Root == nil {
  142. cs.Root = newNode
  143. return
  144. }
  145. cs.makeWay(cs.Root, newNode)
  146. return
  147. }
  148. // makeWay will adjust the Min and Max of this colStoreNode's Col to
  149. // make way for a new colStoreNode's Col. If necessary it will
  150. // generate an additional colStoreNode with a new Col covering the
  151. // "tail" portion of this colStoreNode's Col should the new node lay
  152. // completely within the range of this one, but without reaching its
  153. // maximum extent.
  154. func (cs *ColStore) makeWay(node1, node2 *colStoreNode) {
  155. switch {
  156. case node1.Col.Max < node2.Col.Min:
  157. // The new node2 starts after this one ends, there's no overlap
  158. //
  159. // Node1 |----|
  160. // Node2 |----|
  161. if node1.Next != nil {
  162. cs.makeWay(node1.Next, node2)
  163. return
  164. }
  165. node1.Next = node2
  166. node2.Prev = node1
  167. return
  168. case node1.Col.Min > node2.Col.Max:
  169. // The new node2 ends before this one begins, there's no overlap
  170. //
  171. // Node1 |-----|
  172. // Node2 |----|
  173. if node1.Prev != nil {
  174. cs.makeWay(node1.Prev, node2)
  175. return
  176. }
  177. node1.Prev = node2
  178. node2.Next = node1
  179. return
  180. case node1.Col.Min == node2.Col.Min && node1.Col.Max == node2.Col.Max:
  181. // Exact match
  182. //
  183. // Node1 |xxx|
  184. // Node2 |---|
  185. if node1.Prev != nil {
  186. node1.Prev.Next = node2
  187. node2.Prev = node1.Prev
  188. node1.Prev = nil
  189. }
  190. if node1.Next != nil {
  191. node1.Next.Prev = node2
  192. node2.Next = node1.Next
  193. node1.Next = nil
  194. }
  195. if cs.Root == node1 {
  196. cs.Root = node2
  197. }
  198. case node1.Col.Min < node2.Col.Min && node1.Col.Max > node2.Col.Max:
  199. // The new node2 bisects this one:
  200. //
  201. // Node1 |---xx---|
  202. // Node2 |--|
  203. newCol := node1.Col.copyToRange(node2.Col.Max+1, node1.Col.Max)
  204. newNode := &colStoreNode{Col: newCol, Prev: node2, Next: node1.Next}
  205. node1.Col.Max = node2.Col.Min - 1
  206. node1.Next = node2
  207. node2.Prev = node1
  208. node2.Next = newNode
  209. return
  210. case node1.Col.Max >= node2.Col.Min && node1.Col.Min < node2.Col.Min:
  211. // The new node2 overlaps this one at some point above it's minimum:
  212. //
  213. // Node1 |----xx|
  214. // Node2 |-------|
  215. node1.Col.Max = node2.Col.Min - 1
  216. if node1.Next != nil {
  217. // Break the link to this node, which prevents
  218. // us looping back and forth forever
  219. node1.Next.Prev = nil
  220. cs.makeWay(node1.Next, node2)
  221. }
  222. node1.Next = node2
  223. node2.Prev = node1
  224. return
  225. case node1.Col.Min <= node2.Col.Max && node1.Col.Min > node2.Col.Min:
  226. // The new node2 overlaps this one at some point below it's maximum:
  227. //
  228. // Node1: |------|
  229. // Node2: |----xx|
  230. node1.Col.Min = node2.Col.Max + 1
  231. if node1.Prev != nil {
  232. // Break the link to this node, which prevents
  233. // us looping back and forth forever
  234. node1.Prev.Next = nil
  235. cs.makeWay(node1.Prev, node2)
  236. }
  237. node1.Prev = node2
  238. node2.Next = node1
  239. return
  240. }
  241. return
  242. }