Selaa lähdekoodia

New feature: group and ungroup sheets support

New functions `GroupSheets` and `UngroupSheets` added
Refactor sheet index calculation
xuri 6 vuotta sitten
vanhempi
commit
8b2d4cb697
1 muutettua tiedostoa jossa 109 lisäystä ja 46 poistoa
  1. 109 46
      sheet.go

+ 109 - 46
sheet.go

@@ -275,13 +275,20 @@ func (f *File) SetActiveSheet(index int) {
 // GetActiveSheetIndex provides a function to get active sheet index of the
 // XLSX. If not found the active sheet will be return integer 0.
 func (f *File) GetActiveSheetIndex() int {
-	for idx, name := range f.GetSheetMap() {
-		xlsx, _ := f.workSheetReader(name)
-		for _, sheetView := range xlsx.SheetViews.SheetView {
-			if sheetView.TabSelected {
-				return idx
+	wb := f.workbookReader()
+	if wb != nil {
+		view := wb.BookViews.WorkBookView
+		sheets := wb.Sheets.Sheet
+		var activeTab int
+		if len(view) > 0 {
+			activeTab = view[0].ActiveTab
+			if len(sheets) > activeTab && sheets[activeTab].SheetID != 0 {
+				return sheets[activeTab].SheetID
 			}
 		}
+		if len(wb.Sheets.Sheet) == 1 {
+			return wb.Sheets.Sheet[0].SheetID
+		}
 	}
 	return 0
 }
@@ -308,34 +315,26 @@ func (f *File) SetSheetName(oldName, newName string) {
 // worksheet index. If given sheet index is invalid, will return an empty
 // string.
 func (f *File) GetSheetName(index int) string {
-	content := f.workbookReader()
-	rels := f.workbookRelsReader()
-	for _, rel := range rels.Relationships {
-		rID, _ := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(rel.Target, "worksheets/sheet"), ".xml"))
-		if rID == index {
-			for _, v := range content.Sheets.Sheet {
-				if v.ID == rel.ID {
-					return v.Name
-				}
+	wb := f.workbookReader()
+	if wb != nil {
+		for _, sheet := range wb.Sheets.Sheet {
+			if sheet.SheetID == index {
+				return sheet.Name
 			}
 		}
 	}
 	return ""
 }
 
-// GetSheetIndex provides a function to get worksheet index of XLSX by given sheet
-// name. If given worksheet name is invalid, will return an integer type value
-// 0.
+// GetSheetIndex provides a function to get worksheet index of XLSX by given
+// sheet name. If given worksheet name is invalid, will return an integer type
+// value 0.
 func (f *File) GetSheetIndex(name string) int {
-	content := f.workbookReader()
-	rels := f.workbookRelsReader()
-	for _, v := range content.Sheets.Sheet {
-		if v.Name == name {
-			for _, rel := range rels.Relationships {
-				if v.ID == rel.ID {
-					rID, _ := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(rel.Target, "worksheets/sheet"), ".xml"))
-					return rID
-				}
+	wb := f.workbookReader()
+	if wb != nil {
+		for _, sheet := range wb.Sheets.Sheet {
+			if sheet.Name == trimSheetName(name) {
+				return sheet.SheetID
 			}
 		}
 	}
@@ -354,16 +353,11 @@ func (f *File) GetSheetIndex(name string) int {
 //    }
 //
 func (f *File) GetSheetMap() map[int]string {
-	content := f.workbookReader()
-	rels := f.workbookRelsReader()
+	wb := f.workbookReader()
 	sheetMap := map[int]string{}
-	for _, v := range content.Sheets.Sheet {
-		for _, rel := range rels.Relationships {
-			relStr := strings.SplitN(rel.Target, "worksheets/sheet", 2)
-			if rel.ID == v.ID && len(relStr) == 2 {
-				rID, _ := strconv.Atoi(strings.TrimSuffix(relStr[1], ".xml"))
-				sheetMap[rID] = v.Name
-			}
+	if wb != nil {
+		for _, sheet := range wb.Sheets.Sheet {
+			sheetMap[sheet.SheetID] = sheet.Name
 		}
 	}
 	return sheetMap
@@ -411,19 +405,31 @@ func (f *File) SetSheetBackground(sheet, picture string) error {
 // value of the deleted worksheet, it will cause a file error when you open it.
 // This function will be invalid when only the one worksheet is left.
 func (f *File) DeleteSheet(name string) {
-	content := f.workbookReader()
-	for k, v := range content.Sheets.Sheet {
-		if v.Name == trimSheetName(name) && len(content.Sheets.Sheet) > 1 {
-			content.Sheets.Sheet = append(content.Sheets.Sheet[:k], content.Sheets.Sheet[k+1:]...)
-			sheet := "xl/worksheets/sheet" + strconv.Itoa(v.SheetID) + ".xml"
-			rels := "xl/worksheets/_rels/sheet" + strconv.Itoa(v.SheetID) + ".xml.rels"
-			target := f.deleteSheetFromWorkbookRels(v.ID)
+	if f.SheetCount == 1 || f.GetSheetIndex(name) == 0 {
+		return
+	}
+	sheetName := trimSheetName(name)
+	wb := f.workbookReader()
+	wbRels := f.workbookRelsReader()
+	for idx, sheet := range wb.Sheets.Sheet {
+		if sheet.Name == sheetName {
+			wb.Sheets.Sheet = append(wb.Sheets.Sheet[:idx], wb.Sheets.Sheet[idx+1:]...)
+			var sheetXML, rels string
+			if wbRels != nil {
+				for _, rel := range wbRels.Relationships {
+					if rel.ID == sheet.ID {
+						sheetXML = fmt.Sprintf("xl/%s", rel.Target)
+						rels = strings.Replace(fmt.Sprintf("xl/%s.rels", rel.Target), "xl/worksheets/", "xl/worksheets/_rels/", -1)
+					}
+				}
+			}
+			target := f.deleteSheetFromWorkbookRels(sheet.ID)
 			f.deleteSheetFromContentTypes(target)
-			f.deleteCalcChain(v.SheetID, "") // Delete CalcChain
-			delete(f.sheetMap, name)
-			delete(f.XLSX, sheet)
+			f.deleteCalcChain(sheet.SheetID, "") // Delete CalcChain
+			delete(f.sheetMap, sheetName)
+			delete(f.XLSX, sheetXML)
 			delete(f.XLSX, rels)
-			delete(f.Sheet, sheet)
+			delete(f.Sheet, sheetXML)
 			f.SheetCount--
 		}
 	}
@@ -1285,6 +1291,63 @@ func (f *File) GetDefinedName() []DefinedName {
 	return definedNames
 }
 
+// GroupSheets provides a function to group worksheets by given worksheets
+// name. Group worksheets must contain an active worksheet.
+func (f *File) GroupSheets(sheets []string) error {
+	// check an active worksheet in group worksheets
+	var inActiveSheet bool
+	activeSheet := f.GetActiveSheetIndex()
+	sheetMap := f.GetSheetMap()
+	for idx, sheetName := range sheetMap {
+		for _, s := range sheets {
+			if s == sheetName && idx == activeSheet {
+				inActiveSheet = true
+			}
+		}
+	}
+	if !inActiveSheet {
+		return errors.New("group worksheet must contain an active worksheet")
+	}
+	// check worksheet exists
+	ws := []*xlsxWorksheet{}
+	for _, sheet := range sheets {
+		xlsx, err := f.workSheetReader(sheet)
+		if err != nil {
+			return err
+		}
+		ws = append(ws, xlsx)
+	}
+	for _, s := range ws {
+		sheetViews := s.SheetViews.SheetView
+		if len(sheetViews) > 0 {
+			for idx := range sheetViews {
+				s.SheetViews.SheetView[idx].TabSelected = true
+			}
+			continue
+		}
+	}
+	return nil
+}
+
+// UngroupSheets provides a function to ungroup worksheets.
+func (f *File) UngroupSheets() error {
+	activeSheet := f.GetActiveSheetIndex()
+	sheetMap := f.GetSheetMap()
+	for sheetID, sheet := range sheetMap {
+		if activeSheet == sheetID {
+			continue
+		}
+		xlsx, _ := f.workSheetReader(sheet)
+		sheetViews := xlsx.SheetViews.SheetView
+		if len(sheetViews) > 0 {
+			for idx := range sheetViews {
+				xlsx.SheetViews.SheetView[idx].TabSelected = false
+			}
+		}
+	}
+	return nil
+}
+
 // workSheetRelsReader provides a function to get the pointer to the structure
 // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
 func (f *File) workSheetRelsReader(path string) *xlsxWorkbookRels {