OSDN Git Service

Huge refactorig for consistent col/row numbering (#356)
[excelize/excelize.git] / adjust.go
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 }