adjust.go 8.2 KB
Newer Older
1
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
xurime's avatar
xurime 已提交
2 3 4 5
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
xurime's avatar
xurime 已提交
6
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
xurime's avatar
xurime 已提交
7
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
xurime's avatar
xurime 已提交
8 9
// complex components by high compatibility, and provided streaming API for
// generating or reading data from a worksheet with huge amounts of data. This
10
// library needs Go version 1.15 or later.
xurime's avatar
xurime 已提交
11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
package excelize

type adjustDirection bool

const (
	columns adjustDirection = false
	rows    adjustDirection = true
)

// adjustHelper provides a function to adjust rows and columns dimensions,
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
// columns.
//
// sheet: Worksheet name that we're editing
// column: Index number of the column we're inserting/deleting before
// row: Index number of the row we're inserting/deleting before
// offset: Number of rows/column to insert/delete negative values indicate deletion
//
30
// TODO: adjustPageBreaks, adjustComments, adjustDataValidations, adjustProtectedCells
31
//
32
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) error {
33
	ws, err := f.workSheetReader(sheet)
xurime's avatar
xurime 已提交
34 35 36
	if err != nil {
		return err
	}
37
	sheetID := f.getSheetID(sheet)
38
	if dir == rows {
39
		f.adjustRowDimensions(ws, num, offset)
40
	} else {
41
		f.adjustColDimensions(ws, num, offset)
42
	}
43 44
	f.adjustHyperlinks(ws, sheet, dir, num, offset)
	if err = f.adjustMergeCells(ws, dir, num, offset); err != nil {
45 46
		return err
	}
47
	if err = f.adjustAutoFilter(ws, dir, num, offset); err != nil {
48 49
		return err
	}
50
	if err = f.adjustCalcChain(dir, num, offset, sheetID); err != nil {
51 52
		return err
	}
53 54
	checkSheet(ws)
	_ = checkRow(ws)
55

56 57
	if ws.MergeCells != nil && len(ws.MergeCells.Cells) == 0 {
		ws.MergeCells = nil
58 59
	}

60
	return nil
61 62 63 64
}

// adjustColDimensions provides a function to update column dimensions when
// inserting or deleting rows or columns.
65 66 67
func (f *File) adjustColDimensions(ws *xlsxWorksheet, col, offset int) {
	for rowIdx := range ws.SheetData.Row {
		for colIdx, v := range ws.SheetData.Row[rowIdx].C {
68 69 70
			cellCol, cellRow, _ := CellNameToCoordinates(v.R)
			if col <= cellCol {
				if newCol := cellCol + offset; newCol > 0 {
71
					ws.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)
72 73 74 75 76 77 78 79
				}
			}
		}
	}
}

// adjustRowDimensions provides a function to update row dimensions when
// inserting or deleting rows or columns.
80 81 82
func (f *File) adjustRowDimensions(ws *xlsxWorksheet, row, offset int) {
	for i := range ws.SheetData.Row {
		r := &ws.SheetData.Row[i]
83
		if newRow := r.R + offset; r.R >= row && newRow > 0 {
xurime's avatar
xurime 已提交
84
			f.ajustSingleRowDimensions(r, newRow)
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
		}
	}
}

// ajustSingleRowDimensions provides a function to ajust single row dimensions.
func (f *File) ajustSingleRowDimensions(r *xlsxRow, num int) {
	r.R = num
	for i, col := range r.C {
		colName, _, _ := SplitCellName(col.R)
		r.C[i].R, _ = JoinCellName(colName, num)
	}
}

// adjustHyperlinks provides a function to update hyperlinks when inserting or
// deleting rows or columns.
100
func (f *File) adjustHyperlinks(ws *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
101
	// short path
102
	if ws.Hyperlinks == nil || len(ws.Hyperlinks.Hyperlink) == 0 {
103 104 105 106 107
		return
	}

	// order is important
	if offset < 0 {
108 109
		for i := len(ws.Hyperlinks.Hyperlink) - 1; i >= 0; i-- {
			linkData := ws.Hyperlinks.Hyperlink[i]
110 111 112 113
			colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)

			if (dir == rows && num == rowNum) || (dir == columns && num == colNum) {
				f.deleteSheetRelationships(sheet, linkData.RID)
114 115 116
				if len(ws.Hyperlinks.Hyperlink) > 1 {
					ws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink[:i],
						ws.Hyperlinks.Hyperlink[i+1:]...)
117
				} else {
118
					ws.Hyperlinks = nil
119 120 121 122
				}
			}
		}
	}
123
	if ws.Hyperlinks == nil {
124 125
		return
	}
126 127
	for i := range ws.Hyperlinks.Hyperlink {
		link := &ws.Hyperlinks.Hyperlink[i] // get reference
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
		colNum, rowNum, _ := CellNameToCoordinates(link.Ref)
		if dir == rows {
			if rowNum >= num {
				link.Ref, _ = CoordinatesToCellName(colNum, rowNum+offset)
			}
		} else {
			if colNum >= num {
				link.Ref, _ = CoordinatesToCellName(colNum+offset, rowNum)
			}
		}
	}
}

// adjustAutoFilter provides a function to update the auto filter when
// inserting or deleting rows or columns.
143 144
func (f *File) adjustAutoFilter(ws *xlsxWorksheet, dir adjustDirection, num, offset int) error {
	if ws.AutoFilter == nil {
145
		return nil
146 147
	}

T
three 已提交
148
	coordinates, err := areaRefToCoordinates(ws.AutoFilter.Ref)
149
	if err != nil {
150
		return err
151
	}
152
	x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
153

154
	if (dir == rows && y1 == num && offset < 0) || (dir == columns && x1 == num && x2 == num) {
155 156 157
		ws.AutoFilter = nil
		for rowIdx := range ws.SheetData.Row {
			rowData := &ws.SheetData.Row[rowIdx]
158
			if rowData.R > y1 && rowData.R <= y2 {
159 160 161
				rowData.Hidden = false
			}
		}
162
		return nil
163 164
	}

165 166 167
	coordinates = f.adjustAutoFilterHelper(dir, coordinates, num, offset)
	x1, y1, x2, y2 = coordinates[0], coordinates[1], coordinates[2], coordinates[3]

168
	if ws.AutoFilter.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
169 170 171 172 173 174 175 176 177
		return err
	}
	return nil
}

// adjustAutoFilterHelper provides a function for adjusting auto filter to
// compare and calculate cell axis by the given adjust direction, operation
// axis and offset.
func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, num, offset int) []int {
178
	if dir == rows {
179 180
		if coordinates[1] >= num {
			coordinates[1] += offset
181
		}
182 183
		if coordinates[3] >= num {
			coordinates[3] += offset
184 185
		}
	} else {
186 187
		if coordinates[2] >= num {
			coordinates[2] += offset
188 189
		}
	}
190 191
	return coordinates
}
192 193 194

// adjustMergeCells provides a function to update merged cells when inserting
// or deleting rows or columns.
195 196
func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, offset int) error {
	if ws.MergeCells == nil {
197
		return nil
198 199
	}

200 201
	for i := 0; i < len(ws.MergeCells.Cells); i++ {
		areaData := ws.MergeCells.Cells[i]
T
three 已提交
202
		coordinates, err := areaRefToCoordinates(areaData.Ref)
203
		if err != nil {
204
			return err
205
		}
206
		x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
207
		if dir == rows {
208
			if y1 == num && y2 == num && offset < 0 {
209
				f.deleteMergeCell(ws, i)
210
				i--
211 212 213
			}
			y1 = f.adjustMergeCellsHelper(y1, num, offset)
			y2 = f.adjustMergeCellsHelper(y2, num, offset)
214
		} else {
215
			if x1 == num && x2 == num && offset < 0 {
216
				f.deleteMergeCell(ws, i)
217
				i--
218
			}
219 220
			x1 = f.adjustMergeCellsHelper(x1, num, offset)
			x2 = f.adjustMergeCellsHelper(x2, num, offset)
221
		}
T
three 已提交
222
		if x1 == x2 && y1 == y2 && i >= 0 {
223
			f.deleteMergeCell(ws, i)
224
			i--
225 226
		}
		if areaData.Ref, err = f.coordinatesToAreaRef([]int{x1, y1, x2, y2}); err != nil {
227
			return err
228
		}
229 230 231
	}
	return nil
}
232

233 234 235 236
// adjustMergeCellsHelper provides a function for adjusting merge cells to
// compare and calculate cell axis by the given pivot, operation axis and
// offset.
func (f *File) adjustMergeCellsHelper(pivot, num, offset int) int {
T
three 已提交
237
	if pivot > num {
238 239 240
		pivot += offset
		if pivot < 1 {
			return 1
241
		}
242 243 244 245
		return pivot
	}
	return pivot
}
246

247
// deleteMergeCell provides a function to delete merged cell by given index.
248 249 250 251
func (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {
	if len(ws.MergeCells.Cells) > idx {
		ws.MergeCells.Cells = append(ws.MergeCells.Cells[:idx], ws.MergeCells.Cells[idx+1:]...)
		ws.MergeCells.Count = len(ws.MergeCells.Cells)
252 253
	}
}
254 255 256

// adjustCalcChain provides a function to update the calculation chain when
// inserting or deleting rows or columns.
257
func (f *File) adjustCalcChain(dir adjustDirection, num, offset, sheetID int) error {
258 259 260 261
	if f.CalcChain == nil {
		return nil
	}
	for index, c := range f.CalcChain.C {
262 263 264
		if c.I != sheetID {
			continue
		}
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
		colNum, rowNum, err := CellNameToCoordinates(c.R)
		if err != nil {
			return err
		}
		if dir == rows && num <= rowNum {
			if newRow := rowNum + offset; newRow > 0 {
				f.CalcChain.C[index].R, _ = CoordinatesToCellName(colNum, newRow)
			}
		}
		if dir == columns && num <= colNum {
			if newCol := colNum + offset; newCol > 0 {
				f.CalcChain.C[index].R, _ = CoordinatesToCellName(newCol, rowNum)
			}
		}
	}
	return nil
}