Skip to content

Commit

Permalink
Implement MoveSheet function to sheet.go and remove the SwapSheets fu…
Browse files Browse the repository at this point in the history
…nction
  • Loading branch information
xuri committed Sep 30, 2024
1 parent 6b54994 commit 219dea2
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 271 deletions.
188 changes: 0 additions & 188 deletions excelize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1607,155 +1607,6 @@ func TestAttrValToInt(t *testing.T) {
assert.EqualError(t, err, `strconv.Atoi: parsing "s": invalid syntax`)
}

func TestMoveSheet(t *testing.T) {
f := NewFile()

for i := 2; i < 6; i++ {
f.NewSheet("Sheet" + strconv.Itoa(i))
}

sheetList := f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet2", "Sheet3", "Sheet4", "Sheet5"}, sheetList)

// Move target to first position
err := f.MoveSheet(1, 0)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet2", "Sheet1", "Sheet3", "Sheet4", "Sheet5"}, sheetList)
assert.NoError(t, err)
ws, _ := f.workSheetReader("Sheet1")
assert.Equal(t, ws.SheetViews.SheetView[0].TabSelected, false)

// Move target to last position
err = f.MoveSheet(0, f.SheetCount-1)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet3", "Sheet4", "Sheet5", "Sheet2"}, sheetList)
assert.NoError(t, err)

// Move target to same position
err = f.MoveSheet(4, 4)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet3", "Sheet4", "Sheet5", "Sheet2"}, sheetList)
assert.NoError(t, err)

// Move target to non start and end position
err = f.MoveSheet(4, 2)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet3", "Sheet2", "Sheet4", "Sheet5"}, sheetList)
assert.NoError(t, err)

err = f.MoveSheet(4, 1)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "Sheet3", "Sheet2", "Sheet4"}, sheetList)
assert.NoError(t, err)

// MoveSheets and test grouping
err = f.MoveSheet(3, 2)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "Sheet2", "Sheet3", "Sheet4"}, sheetList)
assert.NoError(t, err)
f.SetActiveSheet(2)
f.GroupSheets([]string{"Sheet2", "Sheet3"})
for _, sheet := range []string{"Sheet2", "Sheet3"} {
ws, _ := f.workSheetReader(sheet)
assert.Equal(t, ws.SheetViews.SheetView[0].TabSelected, true)
}

// Test Moving the ChartSheet
err = prepareTestChartSheet(f)
assert.NoError(t, err)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "Sheet2", "Sheet3", "Sheet4", "ChartSheet"}, sheetList)
err = f.MoveSheet(5, 2)
assert.NoError(t, err)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "ChartSheet", "Sheet2", "Sheet3", "Sheet4"}, sheetList)
// Test Moving the Active ChartSheet
f.SetActiveSheet(2)
err = f.MoveSheet(2, 3)
assert.NoError(t, err)

// Move With Error Index
assert.Error(t, f.MoveSheet(0, -1), ErrSheetIdx)
assert.Error(t, f.MoveSheet(1, f.SheetCount), ErrSheetIdx)
assert.Error(t, f.MoveSheet(-1, 0), ErrSheetIdx)
assert.Error(t, f.MoveSheet(f.SheetCount, 0), ErrSheetIdx)

// Test Move with error Workbook
f.WorkBook = nil
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
assert.EqualError(t, f.MoveSheet(0, 1), "XML syntax error on line 1: invalid UTF-8")
}

func TestSwapSheets(t *testing.T) {
f := NewFile()

for i := 2; i < 6; i++ {
f.NewSheet("Sheet" + strconv.Itoa(i))
}

sheetList := f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet2", "Sheet3", "Sheet4", "Sheet5"}, sheetList)

err := f.SwapSheets(0, 1)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet2", "Sheet1", "Sheet3", "Sheet4", "Sheet5"}, sheetList)
assert.NoError(t, err)

err = f.SwapSheets(1, 4)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet2", "Sheet5", "Sheet3", "Sheet4", "Sheet1"}, sheetList)
assert.NoError(t, err)

err = f.SwapSheets(3, 2)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet2", "Sheet5", "Sheet4", "Sheet3", "Sheet1"}, sheetList)
assert.NoError(t, err)

err = f.SwapSheets(0, f.SheetCount-1)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "Sheet4", "Sheet3", "Sheet2"}, sheetList)
assert.NoError(t, err)

// Test SwapSheets with same index
err = f.SwapSheets(0, 0)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "Sheet4", "Sheet3", "Sheet2"}, sheetList)
assert.NoError(t, err)

// Swap sheet and test grouping
err = f.SwapSheets(3, 2)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "Sheet3", "Sheet4", "Sheet2"}, sheetList)
assert.NoError(t, err)
f.SetActiveSheet(2)
f.GroupSheets([]string{"Sheet2", "Sheet3"})
for _, sheet := range []string{"Sheet2", "Sheet3"} {
ws, _ := f.workSheetReader(sheet)
assert.Equal(t, ws.SheetViews.SheetView[0].TabSelected, true)
}

// Test Swapping the ChartSheet
err = prepareTestChartSheet(f)
assert.NoError(t, err)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "Sheet3", "Sheet4", "Sheet2", "ChartSheet"}, sheetList)
err = f.SwapSheets(5, 2)
assert.NoError(t, err)
sheetList = f.GetSheetList()
assert.Equal(t, []string{"Sheet1", "Sheet5", "ChartSheet", "Sheet4", "Sheet2", "Sheet3"}, sheetList)

// Swap With Error index
assert.Error(t, f.SwapSheets(-1, 0), ErrSheetIdx)
assert.Error(t, f.SwapSheets(0, -1), ErrSheetIdx)
assert.Error(t, f.SwapSheets(f.SheetCount, 0), ErrSheetIdx)
assert.Error(t, f.SwapSheets(0, f.SheetCount), ErrSheetIdx)

// Test Swap with error Workbook
f.WorkBook = nil
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
assert.EqualError(t, f.SwapSheets(0, 1), "XML syntax error on line 1: invalid UTF-8")
}

func prepareTestBook1() (*File, error) {
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
if err != nil {
Expand Down Expand Up @@ -1861,45 +1712,6 @@ func prepareTestBook5(opts Options) (*File, error) {
return f, nil
}

func prepareTestChartSheet(f *File) error {
for idx, row := range [][]interface{}{
{nil, "Apple", "Orange", "Pear"},
{"Small", 2, 3, 3},
{"Normal", 5, 2, 4},
{"Large", 6, 7, 8},
} {
cell, err := CoordinatesToCellName(1, idx+1)
if err != nil {
return err
}
if err := f.SetSheetRow("Sheet1", cell, &row); err != nil {
return err
}
}
if err := f.AddChartSheet("ChartSheet", &Chart{
Type: Col,
Series: []ChartSeries{
{
Name: "Sheet1!$A$2",
Categories: "Sheet1!$B$1:$D$1",
Values: "Sheet1!$B$2:$D$2",
},
},
}, &Chart{
Type: Line,
Series: []ChartSeries{
{
Name: "Sheet1!$A$4",
Categories: "Sheet1!$B$1:$D$1",
Values: "Sheet1!$B$4:$D$4",
},
},
}); err != nil {
return err
}
return nil
}

func fillCells(f *File, sheet string, colCount, rowCount int) error {
for col := 1; col <= colCount; col++ {
for row := 1; row <= rowCount; row++ {
Expand Down
40 changes: 40 additions & 0 deletions sheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,46 @@ func (f *File) DeleteSheet(sheet string) error {
return err
}

// MoveSheet moves a sheet to a specified position in the workbook. The function
// moves the source sheet before the target sheet. After moving, other sheets
// will be shifted to the left or right. If the sheet is already at the target
// position, the function will not perform any action. Not that this function
// will be ungroup all sheets after moving.
func (f *File) MoveSheet(source, target string) error {
if strings.EqualFold(source, target) {
return nil
}
wb, err := f.workbookReader()
if err != nil {
return err
}
sourceIdx, err := f.GetSheetIndex(source)
if err != nil {
return err
}
targetIdx, err := f.GetSheetIndex(target)
if err != nil {
return err
}
if sourceIdx < 0 {
return ErrSheetNotExist{source}
}
if targetIdx < 0 {
return ErrSheetNotExist{target}
}
_ = f.UngroupSheets()
activeSheetName := f.GetSheetName(f.GetActiveSheetIndex())
sourceSheet := wb.Sheets.Sheet[sourceIdx]
wb.Sheets.Sheet = append(wb.Sheets.Sheet[:sourceIdx], wb.Sheets.Sheet[sourceIdx+1:]...)
if targetIdx > sourceIdx {
targetIdx--
}
wb.Sheets.Sheet = append(wb.Sheets.Sheet[:targetIdx], append([]xlsxSheet{sourceSheet}, wb.Sheets.Sheet[targetIdx:]...)...)
activeSheetIdx, _ := f.GetSheetIndex(activeSheetName)
f.SetActiveSheet(activeSheetIdx)
return err
}

// deleteAndAdjustDefinedNames delete and adjust defined name in the workbook
// by given worksheet ID.
func deleteAndAdjustDefinedNames(wb *xlsxWorkbook, deleteLocalSheetID int) {
Expand Down
37 changes: 37 additions & 0 deletions sheet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,43 @@ func TestDeleteSheet(t *testing.T) {
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteSheet2.xlsx")))
}

func TestMoveSheet(t *testing.T) {
f := NewFile()
defer f.Close()
for i := 2; i < 6; i++ {
_, err := f.NewSheet("Sheet" + strconv.Itoa(i))
assert.NoError(t, err)
}
assert.Equal(t, []string{"Sheet1", "Sheet2", "Sheet3", "Sheet4", "Sheet5"}, f.GetSheetList())

// Move target to first position
assert.NoError(t, f.MoveSheet("Sheet2", "Sheet1"))
assert.Equal(t, []string{"Sheet2", "Sheet1", "Sheet3", "Sheet4", "Sheet5"}, f.GetSheetList())
assert.Equal(t, "Sheet1", f.GetSheetName(f.GetActiveSheetIndex()))

// Move target to last position
assert.NoError(t, f.MoveSheet("Sheet2", "Sheet5"))
assert.NoError(t, f.MoveSheet("Sheet5", "Sheet2"))
assert.Equal(t, []string{"Sheet1", "Sheet3", "Sheet4", "Sheet5", "Sheet2"}, f.GetSheetList())

// Move target to same position
assert.NoError(t, f.MoveSheet("Sheet1", "Sheet1"))
assert.Equal(t, []string{"Sheet1", "Sheet3", "Sheet4", "Sheet5", "Sheet2"}, f.GetSheetList())

// Test move sheet with invalid sheet name
assert.Equal(t, ErrSheetNameBlank, f.MoveSheet("", "Sheet2"))
assert.Equal(t, ErrSheetNameBlank, f.MoveSheet("Sheet1", ""))

// Test move sheet on not exists worksheet
assert.Equal(t, ErrSheetNotExist{"SheetN"}, f.MoveSheet("SheetN", "Sheet2"))
assert.Equal(t, ErrSheetNotExist{"SheetN"}, f.MoveSheet("Sheet1", "SheetN"))

// Test move sheet with unsupported workbook charset
f.WorkBook = nil
f.Pkg.Store("xl/workbook.xml", MacintoshCyrillicCharset)
assert.EqualError(t, f.MoveSheet("Sheet2", "Sheet1"), "XML syntax error on line 1: invalid UTF-8")
}

func TestDeleteAndAdjustDefinedNames(t *testing.T) {
deleteAndAdjustDefinedNames(nil, 0)
deleteAndAdjustDefinedNames(&xlsxWorkbook{}, 0)
Expand Down
83 changes: 0 additions & 83 deletions workbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,89 +130,6 @@ func (f *File) UnprotectWorkbook(password ...string) error {
return err
}

// unselectSheetTab deselects the sheet tab at the specified sheet index
// if it is currently active.
func (f *File) unselectSheetTab(sheetIdx int, wb *xlsxWorkbook) {
activeSheetName := f.GetSheetName(f.GetActiveSheetIndex())
if activeSheetName == wb.Sheets.Sheet[sheetIdx].Name {
ws, err := f.workSheetReader(wb.Sheets.Sheet[sheetIdx].Name)
if err != nil {
return
}
ws.SheetViews.SheetView[0].TabSelected = false
}
}

// SwapSheets swaps the position of two sheets in the workbook
// based on their index positions. If the indices of the sheets
// are the same, the function does nothing. Note that the index
// must be greater than or equal to 0 and less than SheetCount.
// For example:
//
// err := f.SwapSheets(1, 3)
// if err != nil {
// // handle the error
// }
func (f *File) SwapSheets(sheet1Idx, sheet2Idx int) error {
if sheet1Idx == sheet2Idx {
return nil
}

wb, err := f.workbookReader()
if err != nil {
return err
}
if sheet1Idx >= f.SheetCount || sheet2Idx >= f.SheetCount || sheet1Idx < 0 || sheet2Idx < 0 {
return ErrSheetIdx
}

// Unselect the tab of a sheet by index
f.unselectSheetTab(sheet1Idx, wb)
f.unselectSheetTab(sheet2Idx, wb)

tempSheet := wb.Sheets.Sheet[sheet1Idx]
wb.Sheets.Sheet[sheet1Idx] = wb.Sheets.Sheet[sheet2Idx]
wb.Sheets.Sheet[sheet2Idx] = tempSheet
return nil
}

// MoveSheet moves a worksheet to a specified position in the workbook.
// The function takes the index of the source sheet and the target index.
// After moving the worksheet to the target index, other sheets will be
// shifted to the left or right. If the sheet is already at the target position,
// the function will not perform any action. Note that the index must be
// greater than or equal to 0 and less than SheetCount.
// For example:
//
// err := f.MoveSheet(0, 3)
// if err != nil {
// // handle the error
// }
func (f *File) MoveSheet(sourceIdx, targetIdx int) error {
if sourceIdx == targetIdx {
return nil
}
wb, err := f.workbookReader()
if err != nil {
return err
}

if sourceIdx >= f.SheetCount || sourceIdx < 0 || targetIdx >= f.SheetCount || targetIdx < 0 {
return ErrSheetIdx
}

// Unselect the tab of a sheet by index
f.unselectSheetTab(targetIdx, wb)
f.unselectSheetTab(sourceIdx, wb)

sourceSheet := wb.Sheets.Sheet[sourceIdx]
wb.Sheets.Sheet = append(wb.Sheets.Sheet[:sourceIdx], wb.Sheets.Sheet[sourceIdx+1:]...)
sheetsTemp := append([]xlsxSheet{sourceSheet}, wb.Sheets.Sheet[targetIdx:]...)
wb.Sheets.Sheet = append(wb.Sheets.Sheet[:targetIdx], sheetsTemp...)

return nil
}

// setWorkbook update workbook property of the spreadsheet. Maximum 31
// characters are allowed in sheet title.
func (f *File) setWorkbook(name string, sheetID, rid int) {
Expand Down

0 comments on commit 219dea2

Please sign in to comment.