mirror of
https://github.com/unidoc/unioffice.git
synced 2025-05-02 22:17:07 +08:00
spreadsheet: support adding/removing an auto filter
This commit is contained in:
parent
d8554f54de
commit
f70810321d
35
_examples/spreadsheet/sort-filter/main.go
Normal file
35
_examples/spreadsheet/sort-filter/main.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2017 Baliance. All rights reserved.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"baliance.com/gooxml/spreadsheet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ss := spreadsheet.New()
|
||||
// add a single sheet
|
||||
sheet := ss.AddSheet()
|
||||
hdrRow := sheet.AddRow()
|
||||
hdrRow.AddCell().SetString("Product Name")
|
||||
hdrRow.AddCell().SetString("Quantity")
|
||||
hdrRow.AddCell().SetString("Price")
|
||||
sheet.SetAutoFilter("A1:C6")
|
||||
|
||||
// rows
|
||||
for r := 0; r < 5; r++ {
|
||||
row := sheet.AddRow()
|
||||
row.AddCell().SetString(fmt.Sprintf("Product %d", r+1))
|
||||
row.AddCell().SetNumber(float64(r + 2))
|
||||
row.AddCell().SetNumber(float64(3*r + 1))
|
||||
|
||||
}
|
||||
|
||||
if err := ss.Validate(); err != nil {
|
||||
log.Fatalf("error validating sheet: %s", err)
|
||||
}
|
||||
|
||||
ss.SaveToFile("sort-filter.xlsx")
|
||||
}
|
BIN
_examples/spreadsheet/sort-filter/sort-filter.xlsx
Normal file
BIN
_examples/spreadsheet/sort-filter/sort-filter.xlsx
Normal file
Binary file not shown.
@ -8,6 +8,7 @@
|
||||
package spreadsheet
|
||||
|
||||
import sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
|
||||
import "baliance.com/gooxml"
|
||||
|
||||
// DefinedName is a named range, formula, etc.
|
||||
type DefinedName struct {
|
||||
@ -28,3 +29,18 @@ func (d DefinedName) Name() string {
|
||||
func (d DefinedName) Content() string {
|
||||
return d.x.Content
|
||||
}
|
||||
|
||||
// SetContent sets the defined name content.
|
||||
func (d DefinedName) SetContent(s string) {
|
||||
d.x.Content = s
|
||||
}
|
||||
|
||||
// SetHidden marks the defined name as hidden.
|
||||
func (d DefinedName) SetHidden(b bool) {
|
||||
d.x.HiddenAttr = gooxml.Bool(b)
|
||||
}
|
||||
|
||||
// SetHidden marks the defined name as hidden.
|
||||
func (d DefinedName) SetLocalSheetID(id uint32) {
|
||||
d.x.LocalSheetIdAttr = gooxml.Uint32(id)
|
||||
}
|
||||
|
@ -25,6 +25,11 @@ type Sheet struct {
|
||||
x *sml.Worksheet
|
||||
}
|
||||
|
||||
// X returns the inner wrapped XML type.
|
||||
func (s Sheet) X() *sml.Worksheet {
|
||||
return s.x
|
||||
}
|
||||
|
||||
// Row will return a row with a given row number, creating a new row if
|
||||
// necessary.
|
||||
func (s Sheet) Row(rowNum uint32) Row {
|
||||
@ -193,3 +198,56 @@ func (s Sheet) RangeReference(n string) string {
|
||||
to := fmt.Sprintf("$%s$%d", tc, tr)
|
||||
return fmt.Sprintf(`'%s'!%s:%s`, s.Name(), from, to)
|
||||
}
|
||||
|
||||
const autoFilterName = "_xlnm._FilterDatabase"
|
||||
|
||||
// ClearAutoFilter removes the autofilters from the sheet.
|
||||
func (s Sheet) ClearAutoFilter() {
|
||||
s.x.AutoFilter = nil
|
||||
sn := "'" + s.Name() + "'!"
|
||||
// see if we have a defined auto filter name for the sheet
|
||||
for _, dn := range s.w.DefinedNames() {
|
||||
if dn.Name() == autoFilterName {
|
||||
if strings.HasPrefix(dn.Content(), sn) {
|
||||
s.w.RemoveDefinedName(dn)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetAutoFilter creates autofilters on the sheet. These are the automatic
|
||||
// filters that are common for a header row. The RangeRef should be of the form
|
||||
// "A1:C5" and cover the entire range of cells to be filtered, not just the
|
||||
// header. SetAutoFilter replaces any existing auto filter on the sheet.
|
||||
func (s Sheet) SetAutoFilter(rangeRef string) {
|
||||
// this should have no $ in it
|
||||
rangeRef = strings.Replace(rangeRef, "$", "", -1)
|
||||
|
||||
s.x.AutoFilter = sml.NewCT_AutoFilter()
|
||||
s.x.AutoFilter.RefAttr = gooxml.String(rangeRef)
|
||||
sn := "'" + s.Name() + "'!"
|
||||
var sdn DefinedName
|
||||
|
||||
// see if we already have a defined auto filter name for the sheet
|
||||
for _, dn := range s.w.DefinedNames() {
|
||||
if dn.Name() == autoFilterName {
|
||||
if strings.HasPrefix(dn.Content(), sn) {
|
||||
sdn = dn
|
||||
// name must match, but make sure rangeRef matches as well
|
||||
sdn.SetContent(s.RangeReference(rangeRef))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// no existing name found, so add a new one
|
||||
if sdn.X() == nil {
|
||||
sdn = s.w.AddDefinedName(autoFilterName, s.RangeReference(rangeRef))
|
||||
}
|
||||
|
||||
for i, ws := range s.w.xws {
|
||||
if ws == s.x {
|
||||
sdn.SetLocalSheetID(uint32(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,3 +82,42 @@ func TestRowNumberValidation(t *testing.T) {
|
||||
t.Errorf("expected validation error with identically numbered rows")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoFilter(t *testing.T) {
|
||||
wb := spreadsheet.New()
|
||||
sheet := wb.AddSheet()
|
||||
if len(wb.DefinedNames()) != 0 {
|
||||
t.Errorf("expected no defined names for new workbook")
|
||||
}
|
||||
sheet.SetAutoFilter("A1:C10")
|
||||
if len(wb.DefinedNames()) != 1 {
|
||||
t.Errorf("expected a new defined names for the autofilter")
|
||||
}
|
||||
dn := wb.DefinedNames()[0]
|
||||
expContent := "'Sheet 1'!$A$1:$C$10"
|
||||
if dn.Content() != expContent {
|
||||
t.Errorf("expected defined name content = '%s', got %s", expContent, dn.Content())
|
||||
}
|
||||
|
||||
sheet.SetAutoFilter("A1:B10")
|
||||
expContent = "'Sheet 1'!$A$1:$B$10"
|
||||
// setting the filter again should re-write the defined name and not create a new one
|
||||
if len(wb.DefinedNames()) != 1 {
|
||||
t.Errorf("expected a new defined names for the autofilter")
|
||||
}
|
||||
dn = wb.DefinedNames()[0]
|
||||
// but the content should have changed
|
||||
if dn.Content() != expContent {
|
||||
t.Errorf("expected defined name content = '%s', got %s", expContent, dn.Content())
|
||||
}
|
||||
|
||||
sheet.ClearAutoFilter()
|
||||
if len(wb.DefinedNames()) != 0 {
|
||||
t.Errorf("clearing the filter should have removed the defined name")
|
||||
}
|
||||
|
||||
if sheet.X().AutoFilter != nil {
|
||||
t.Errorf("autofilter should have been nil after clear")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -351,6 +351,22 @@ func (wb *Workbook) AddDefinedName(name, ref string) DefinedName {
|
||||
return DefinedName{dn}
|
||||
}
|
||||
|
||||
// RemoveDefinedName removes an existing defined name.
|
||||
func (wb *Workbook) RemoveDefinedName(dn DefinedName) error {
|
||||
if dn.X() == nil {
|
||||
return errors.New("attempt to remove nil DefinedName")
|
||||
}
|
||||
for i, sdn := range wb.x.DefinedNames.DefinedName {
|
||||
if sdn == dn.X() {
|
||||
copy(wb.x.DefinedNames.DefinedName[i:], wb.x.DefinedNames.DefinedName[i+1:])
|
||||
wb.x.DefinedNames.DefinedName[len(wb.x.DefinedNames.DefinedName)-1] = nil
|
||||
wb.x.DefinedNames.DefinedName = wb.x.DefinedNames.DefinedName[:len(wb.x.DefinedNames.DefinedName)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("defined name not found")
|
||||
}
|
||||
|
||||
// DefinedNames returns a slice of all defined names in the workbook.
|
||||
func (wb *Workbook) DefinedNames() []DefinedName {
|
||||
if wb.x.DefinedNames == nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user