|
| 1 | +package excelize |
| 2 | + |
| 3 | +import ( |
| 4 | + "strings" |
| 5 | +) |
| 6 | + |
| 7 | +type adjustDirection bool |
| 8 | + |
| 9 | +const ( |
| 10 | + columns adjustDirection = false |
| 11 | + rows adjustDirection = true |
| 12 | +) |
| 13 | + |
| 14 | +// adjustHelper provides a function to adjust rows and columns dimensions, |
| 15 | +// hyperlinks, merged cells and auto filter when inserting or deleting rows or |
| 16 | +// columns. |
| 17 | +// |
| 18 | +// sheet: Worksheet name that we're editing |
| 19 | +// column: Index number of the column we're inserting/deleting before |
| 20 | +// row: Index number of the row we're inserting/deleting before |
| 21 | +// offset: Number of rows/column to insert/delete negative values indicate deletion |
| 22 | +// |
| 23 | +// TODO: adjustCalcChain, adjustPageBreaks, adjustComments, |
| 24 | +// adjustDataValidations, adjustProtectedCells |
| 25 | +// |
| 26 | +func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) { |
| 27 | + xlsx := f.workSheetReader(sheet) |
| 28 | + |
| 29 | + if dir == rows { |
| 30 | + f.adjustRowDimensions(xlsx, num, offset) |
| 31 | + } else { |
| 32 | + f.adjustColDimensions(xlsx, num, offset) |
| 33 | + } |
| 34 | + f.adjustHyperlinks(xlsx, sheet, dir, num, offset) |
| 35 | + f.adjustMergeCells(xlsx, dir, num, offset) |
| 36 | + f.adjustAutoFilter(xlsx, dir, num, offset) |
| 37 | + |
| 38 | + checkSheet(xlsx) |
| 39 | + checkRow(xlsx) |
| 40 | +} |
| 41 | + |
| 42 | +// adjustColDimensions provides a function to update column dimensions when |
| 43 | +// inserting or deleting rows or columns. |
| 44 | +func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) { |
| 45 | + for rowIdx := range xlsx.SheetData.Row { |
| 46 | + for colIdx, v := range xlsx.SheetData.Row[rowIdx].C { |
| 47 | + cellCol, cellRow, _ := CellNameToCoordinates(v.R) |
| 48 | + if col <= cellCol { |
| 49 | + if newCol := cellCol + offset; newCol > 0 { |
| 50 | + xlsx.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow) |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +// adjustRowDimensions provides a function to update row dimensions when |
| 58 | +// inserting or deleting rows or columns. |
| 59 | +func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) { |
| 60 | + for i, r := range xlsx.SheetData.Row { |
| 61 | + if newRow := r.R + offset; r.R >= row && newRow > 0 { |
| 62 | + f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], newRow) |
| 63 | + } |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +// ajustSingleRowDimensions provides a function to ajust single row dimensions. |
| 68 | +func (f *File) ajustSingleRowDimensions(r *xlsxRow, num int) { |
| 69 | + r.R = num |
| 70 | + for i, col := range r.C { |
| 71 | + colName, _, _ := SplitCellName(col.R) |
| 72 | + r.C[i].R, _ = JoinCellName(colName, num) |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +// adjustHyperlinks provides a function to update hyperlinks when inserting or |
| 77 | +// deleting rows or columns. |
| 78 | +func (f *File) adjustHyperlinks(xlsx *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) { |
| 79 | + // short path |
| 80 | + if xlsx.Hyperlinks == nil || len(xlsx.Hyperlinks.Hyperlink) == 0 { |
| 81 | + return |
| 82 | + } |
| 83 | + |
| 84 | + // order is important |
| 85 | + if offset < 0 { |
| 86 | + for rowIdx, linkData := range xlsx.Hyperlinks.Hyperlink { |
| 87 | + colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref) |
| 88 | + |
| 89 | + if (dir == rows && num == rowNum) || (dir == columns && num == colNum) { |
| 90 | + f.deleteSheetRelationships(sheet, linkData.RID) |
| 91 | + if len(xlsx.Hyperlinks.Hyperlink) > 1 { |
| 92 | + xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:rowIdx], |
| 93 | + xlsx.Hyperlinks.Hyperlink[rowIdx+1:]...) |
| 94 | + } else { |
| 95 | + xlsx.Hyperlinks = nil |
| 96 | + } |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + if xlsx.Hyperlinks == nil { |
| 102 | + return |
| 103 | + } |
| 104 | + |
| 105 | + for i := range xlsx.Hyperlinks.Hyperlink { |
| 106 | + link := &xlsx.Hyperlinks.Hyperlink[i] // get reference |
| 107 | + colNum, rowNum, _ := CellNameToCoordinates(link.Ref) |
| 108 | + |
| 109 | + if dir == rows { |
| 110 | + if rowNum >= num { |
| 111 | + link.Ref, _ = CoordinatesToCellName(colNum, rowNum+offset) |
| 112 | + } |
| 113 | + } else { |
| 114 | + if colNum >= num { |
| 115 | + link.Ref, _ = CoordinatesToCellName(colNum+offset, rowNum) |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +// adjustAutoFilter provides a function to update the auto filter when |
| 122 | +// inserting or deleting rows or columns. |
| 123 | +func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) { |
| 124 | + if xlsx.AutoFilter == nil { |
| 125 | + return |
| 126 | + } |
| 127 | + |
| 128 | + rng := strings.Split(xlsx.AutoFilter.Ref, ":") |
| 129 | + firstCell := rng[0] |
| 130 | + lastCell := rng[1] |
| 131 | + |
| 132 | + firstCol, firstRow, err := CellNameToCoordinates(firstCell) |
| 133 | + if err != nil { |
| 134 | + panic(err) |
| 135 | + } |
| 136 | + |
| 137 | + lastCol, lastRow, err := CellNameToCoordinates(lastCell) |
| 138 | + if err != nil { |
| 139 | + panic(err) |
| 140 | + } |
| 141 | + |
| 142 | + if (dir == rows && firstRow == num && offset < 0) || (dir == columns && firstCol == num && lastCol == num) { |
| 143 | + xlsx.AutoFilter = nil |
| 144 | + for rowIdx := range xlsx.SheetData.Row { |
| 145 | + rowData := &xlsx.SheetData.Row[rowIdx] |
| 146 | + if rowData.R > firstRow && rowData.R <= lastRow { |
| 147 | + rowData.Hidden = false |
| 148 | + } |
| 149 | + } |
| 150 | + return |
| 151 | + } |
| 152 | + |
| 153 | + if dir == rows { |
| 154 | + if firstRow >= num { |
| 155 | + firstCell, _ = CoordinatesToCellName(firstCol, firstRow+offset) |
| 156 | + } |
| 157 | + if lastRow >= num { |
| 158 | + lastCell, _ = CoordinatesToCellName(lastCol, lastRow+offset) |
| 159 | + } |
| 160 | + } else { |
| 161 | + if lastCol >= num { |
| 162 | + lastCell, _ = CoordinatesToCellName(lastCol+offset, lastRow) |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + xlsx.AutoFilter.Ref = firstCell + ":" + lastCell |
| 167 | +} |
| 168 | + |
| 169 | +// adjustMergeCells provides a function to update merged cells when inserting |
| 170 | +// or deleting rows or columns. |
| 171 | +func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) { |
| 172 | + if xlsx.MergeCells == nil { |
| 173 | + return |
| 174 | + } |
| 175 | + |
| 176 | + for i, areaData := range xlsx.MergeCells.Cells { |
| 177 | + rng := strings.Split(areaData.Ref, ":") |
| 178 | + firstCell := rng[0] |
| 179 | + lastCell := rng[1] |
| 180 | + |
| 181 | + firstCol, firstRow, err := CellNameToCoordinates(firstCell) |
| 182 | + if err != nil { |
| 183 | + panic(err) |
| 184 | + } |
| 185 | + |
| 186 | + lastCol, lastRow, err := CellNameToCoordinates(lastCell) |
| 187 | + if err != nil { |
| 188 | + panic(err) |
| 189 | + } |
| 190 | + |
| 191 | + adjust := func(v int) int { |
| 192 | + if v >= num { |
| 193 | + v += offset |
| 194 | + if v < 1 { |
| 195 | + return 1 |
| 196 | + } |
| 197 | + return v |
| 198 | + } |
| 199 | + return v |
| 200 | + } |
| 201 | + |
| 202 | + if dir == rows { |
| 203 | + firstRow = adjust(firstRow) |
| 204 | + lastRow = adjust(lastRow) |
| 205 | + } else { |
| 206 | + firstCol = adjust(firstCol) |
| 207 | + lastCol = adjust(lastCol) |
| 208 | + } |
| 209 | + |
| 210 | + if firstCol == lastCol && firstRow == lastRow { |
| 211 | + if len(xlsx.MergeCells.Cells) > 1 { |
| 212 | + xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...) |
| 213 | + xlsx.MergeCells.Count = len(xlsx.MergeCells.Cells) |
| 214 | + } else { |
| 215 | + xlsx.MergeCells = nil |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + if firstCell, err = CoordinatesToCellName(firstCol, firstRow); err != nil { |
| 220 | + panic(err) |
| 221 | + } |
| 222 | + |
| 223 | + if lastCell, err = CoordinatesToCellName(lastCol, lastRow); err != nil { |
| 224 | + panic(err) |
| 225 | + } |
| 226 | + |
| 227 | + areaData.Ref = firstCell + ":" + lastCell |
| 228 | + } |
| 229 | +} |
0 commit comments