Skip to content

Commit 78c974d

Browse files
New function GetPanes for get sheet panes and view selection (#1556)
- Breaking changes: rename exported type `PaneOptions` to `Selection` - Update unit tests - Upgrade dependencies package - Add internal error variables - Simplify variable declarations
1 parent 661c0ea commit 78c974d

File tree

10 files changed

+138
-71
lines changed

10 files changed

+138
-71
lines changed

errors.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ func newViewIdxError(viewIndex int) error {
100100
return fmt.Errorf("view index %d out of range", viewIndex)
101101
}
102102

103+
// newUnknownFilterTokenError defined the error message on receiving a unknown
104+
// filter operator token.
105+
func newUnknownFilterTokenError(token string) error {
106+
return fmt.Errorf("unknown operator: %s", token)
107+
}
108+
103109
var (
104110
// ErrStreamSetColWidth defined the error message on set column width in
105111
// stream writing mode.

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ require (
88
github.com/stretchr/testify v1.8.0
99
github.com/xuri/efp v0.0.0-20230422071738-01f4e37c47e9
1010
github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83
11-
golang.org/x/crypto v0.8.0
11+
golang.org/x/crypto v0.9.0
1212
golang.org/x/image v0.5.0
13-
golang.org/x/net v0.9.0
13+
golang.org/x/net v0.10.0
1414
golang.org/x/text v0.9.0
1515
)
1616

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ github.com/xuri/nfp v0.0.0-20230503010013-3f38cdbb0b83/go.mod h1:WwHg+CVyzlv/TX9
2222
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
2323
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
2424
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
25-
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
26-
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
25+
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
26+
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
2727
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
2828
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
2929
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -32,8 +32,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
3232
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
3333
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
3434
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
35-
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
36-
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
35+
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
36+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
3737
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
3838
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
3939
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -43,11 +43,11 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
4343
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4444
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4545
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
46-
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
46+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4747
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
4848
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
4949
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
50-
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
50+
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
5151
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
5252
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
5353
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=

numfmt.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,8 +1018,13 @@ var (
10181018
// applyBuiltInNumFmt provides a function to returns a value after formatted
10191019
// with built-in number format code, or specified sort date format code.
10201020
func (f *File) applyBuiltInNumFmt(c *xlsxC, fmtCode string, numFmtID int, date1904 bool, cellType CellType) string {
1021-
if numFmtID == 14 && f.options != nil && f.options.ShortDatePattern != "" {
1022-
fmtCode = f.options.ShortDatePattern
1021+
if f.options != nil && f.options.ShortDatePattern != "" {
1022+
if numFmtID == 14 {
1023+
fmtCode = f.options.ShortDatePattern
1024+
}
1025+
if numFmtID == 22 {
1026+
fmtCode = fmt.Sprintf("%s hh:mm", f.options.ShortDatePattern)
1027+
}
10231028
}
10241029
return format(c.V, fmtCode, date1904, cellType, f.options)
10251030
}
@@ -1073,9 +1078,6 @@ func (f *File) langNumFmtFuncZhCN(numFmtID int) string {
10731078
// getBuiltInNumFmtCode convert number format index to number format code with
10741079
// specified locale and language.
10751080
func (f *File) getBuiltInNumFmtCode(numFmtID int) (string, bool) {
1076-
if numFmtID == 22 && f.options.ShortDatePattern != "" {
1077-
return fmt.Sprintf("%s hh:mm", f.options.ShortDatePattern), true
1078-
}
10791081
if fmtCode, ok := builtInNumFmt[numFmtID]; ok {
10801082
return fmtCode, true
10811083
}

sheet.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ func (ws *xlsxWorksheet) setPanes(panes *Panes) error {
772772
}
773773
}
774774
var s []*xlsxSelection
775-
for _, p := range panes.Panes {
775+
for _, p := range panes.Selection {
776776
s = append(s, &xlsxSelection{
777777
ActiveCell: p.ActiveCell,
778778
Pane: p.Pane,
@@ -859,7 +859,7 @@ func (ws *xlsxWorksheet) setPanes(panes *Panes) error {
859859
// YSplit: 0,
860860
// TopLeftCell: "B1",
861861
// ActivePane: "topRight",
862-
// Panes: []excelize.PaneOptions{
862+
// Selection: []excelize.Selection{
863863
// {SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
864864
// },
865865
// })
@@ -874,7 +874,7 @@ func (ws *xlsxWorksheet) setPanes(panes *Panes) error {
874874
// YSplit: 9,
875875
// TopLeftCell: "A34",
876876
// ActivePane: "bottomLeft",
877-
// Panes: []excelize.PaneOptions{
877+
// Selection: []excelize.Selection{
878878
// {SQRef: "A11:XFD11", ActiveCell: "A11", Pane: "bottomLeft"},
879879
// },
880880
// })
@@ -889,7 +889,7 @@ func (ws *xlsxWorksheet) setPanes(panes *Panes) error {
889889
// YSplit: 1800,
890890
// TopLeftCell: "N57",
891891
// ActivePane: "bottomLeft",
892-
// Panes: []excelize.PaneOptions{
892+
// Selection: []excelize.Selection{
893893
// {SQRef: "I36", ActiveCell: "I36"},
894894
// {SQRef: "G33", ActiveCell: "G33", Pane: "topRight"},
895895
// {SQRef: "J60", ActiveCell: "J60", Pane: "bottomLeft"},
@@ -908,6 +908,50 @@ func (f *File) SetPanes(sheet string, panes *Panes) error {
908908
return ws.setPanes(panes)
909909
}
910910

911+
// getPanes returns freeze panes, split panes, and views of the worksheet.
912+
func (ws *xlsxWorksheet) getPanes() Panes {
913+
var (
914+
panes Panes
915+
section []Selection
916+
)
917+
if ws.SheetViews == nil || len(ws.SheetViews.SheetView) < 1 {
918+
return panes
919+
}
920+
sw := ws.SheetViews.SheetView[len(ws.SheetViews.SheetView)-1]
921+
for _, s := range sw.Selection {
922+
if s != nil {
923+
section = append(section, Selection{
924+
SQRef: s.SQRef,
925+
ActiveCell: s.ActiveCell,
926+
Pane: s.Pane,
927+
})
928+
}
929+
}
930+
panes.Selection = section
931+
if sw.Pane == nil {
932+
return panes
933+
}
934+
panes.ActivePane = sw.Pane.ActivePane
935+
if sw.Pane.State == "frozen" {
936+
panes.Freeze = true
937+
}
938+
panes.TopLeftCell = sw.Pane.TopLeftCell
939+
panes.XSplit = int(sw.Pane.XSplit)
940+
panes.YSplit = int(sw.Pane.YSplit)
941+
return panes
942+
}
943+
944+
// GetPanes provides a function to get freeze panes, split panes, and worksheet
945+
// views by given worksheet name.
946+
func (f *File) GetPanes(sheet string) (Panes, error) {
947+
var panes Panes
948+
ws, err := f.workSheetReader(sheet)
949+
if err != nil {
950+
return panes, err
951+
}
952+
return ws.getPanes(), err
953+
}
954+
911955
// GetSheetVisible provides a function to get worksheet visible by given worksheet
912956
// name. For example, get visible state of Sheet1:
913957
//

sheet_test.go

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,29 @@ func TestNewSheet(t *testing.T) {
3737
assert.Equal(t, -1, sheetID)
3838
}
3939

40-
func TestSetPanes(t *testing.T) {
40+
func TestPanes(t *testing.T) {
4141
f := NewFile()
4242

4343
assert.NoError(t, f.SetPanes("Sheet1", &Panes{Freeze: false, Split: false}))
4444
_, err := f.NewSheet("Panes 2")
4545
assert.NoError(t, err)
46-
assert.NoError(t, f.SetPanes("Panes 2",
47-
&Panes{
48-
Freeze: true,
49-
Split: false,
50-
XSplit: 1,
51-
YSplit: 0,
52-
TopLeftCell: "B1",
53-
ActivePane: "topRight",
54-
Panes: []PaneOptions{
55-
{SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
56-
},
46+
47+
expected := Panes{
48+
Freeze: true,
49+
Split: false,
50+
XSplit: 1,
51+
YSplit: 0,
52+
TopLeftCell: "B1",
53+
ActivePane: "topRight",
54+
Selection: []Selection{
55+
{SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
5756
},
58-
))
57+
}
58+
assert.NoError(t, f.SetPanes("Panes 2", &expected))
59+
panes, err := f.GetPanes("Panes 2")
60+
assert.NoError(t, err)
61+
assert.Equal(t, expected, panes)
62+
5963
_, err = f.NewSheet("Panes 3")
6064
assert.NoError(t, err)
6165
assert.NoError(t, f.SetPanes("Panes 3",
@@ -66,7 +70,7 @@ func TestSetPanes(t *testing.T) {
6670
YSplit: 1800,
6771
TopLeftCell: "N57",
6872
ActivePane: "bottomLeft",
69-
Panes: []PaneOptions{
73+
Selection: []Selection{
7074
{SQRef: "I36", ActiveCell: "I36"},
7175
{SQRef: "G33", ActiveCell: "G33", Pane: "topRight"},
7276
{SQRef: "J60", ActiveCell: "J60", Pane: "bottomLeft"},
@@ -84,7 +88,7 @@ func TestSetPanes(t *testing.T) {
8488
YSplit: 9,
8589
TopLeftCell: "A34",
8690
ActivePane: "bottomLeft",
87-
Panes: []PaneOptions{
91+
Selection: []Selection{
8892
{SQRef: "A11:XFD11", ActiveCell: "A11", Pane: "bottomLeft"},
8993
},
9094
},
@@ -94,6 +98,26 @@ func TestSetPanes(t *testing.T) {
9498
// Test set panes with invalid sheet name
9599
assert.EqualError(t, f.SetPanes("Sheet:1", &Panes{Freeze: false, Split: false}), ErrSheetNameInvalid.Error())
96100
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetPane.xlsx")))
101+
102+
// Test get panes with empty sheet views
103+
f = NewFile()
104+
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
105+
assert.True(t, ok)
106+
ws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{}
107+
_, err = f.GetPanes("Sheet1")
108+
assert.NoError(t, err)
109+
// Test get panes without panes
110+
ws.(*xlsxWorksheet).SheetViews = &xlsxSheetViews{SheetView: []xlsxSheetView{{}}}
111+
_, err = f.GetPanes("Sheet1")
112+
assert.NoError(t, err)
113+
// Test get panes without sheet views
114+
ws.(*xlsxWorksheet).SheetViews = nil
115+
_, err = f.GetPanes("Sheet1")
116+
assert.NoError(t, err)
117+
// Test get panes on not exists worksheet
118+
_, err = f.GetPanes("SheetN")
119+
assert.EqualError(t, err, "sheet SheetN does not exist")
120+
97121
// Test add pane on empty sheet views worksheet
98122
f = NewFile()
99123
f.checked = nil
@@ -107,7 +131,7 @@ func TestSetPanes(t *testing.T) {
107131
YSplit: 0,
108132
TopLeftCell: "B1",
109133
ActivePane: "topRight",
110-
Panes: []PaneOptions{
134+
Selection: []Selection{
111135
{SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
112136
},
113137
},

sheetview.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,7 @@ func (view *xlsxSheetView) setSheetView(opts *ViewOptions) {
6161
view.TopLeftCell = *opts.TopLeftCell
6262
}
6363
if opts.View != nil {
64-
if _, ok := map[string]interface{}{
65-
"normal": nil,
66-
"pageLayout": nil,
67-
"pageBreakPreview": nil,
68-
}[*opts.View]; ok {
64+
if inStrSlice([]string{"normal", "pageLayout", "pageBreakPreview"}, *opts.View, true) != -1 {
6965
view.View = *opts.View
7066
}
7167
}

stream_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ func TestStreamSetPanes(t *testing.T) {
173173
YSplit: 0,
174174
TopLeftCell: "B1",
175175
ActivePane: "topRight",
176-
Panes: []PaneOptions{
176+
Selection: []Selection{
177177
{SQRef: "K16", ActiveCell: "K16", Pane: "topRight"},
178178
},
179179
}

table.go

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -430,22 +430,23 @@ func (f *File) writeAutoFilter(fc *xlsxFilterColumn, exp []int, tokens []string)
430430
var filters []*xlsxFilter
431431
filters = append(filters, &xlsxFilter{Val: tokens[0]})
432432
fc.Filters = &xlsxFilters{Filter: filters}
433-
} else if len(exp) == 3 && exp[0] == 2 && exp[1] == 1 && exp[2] == 2 {
433+
return
434+
}
435+
if len(exp) == 3 && exp[0] == 2 && exp[1] == 1 && exp[2] == 2 {
434436
// Double equality with "or" operator.
435437
var filters []*xlsxFilter
436438
for _, v := range tokens {
437439
filters = append(filters, &xlsxFilter{Val: v})
438440
}
439441
fc.Filters = &xlsxFilters{Filter: filters}
440-
} else {
441-
// Non default custom filter.
442-
expRel := map[int]int{0: 0, 1: 2}
443-
andRel := map[int]bool{0: true, 1: false}
444-
for k, v := range tokens {
445-
f.writeCustomFilter(fc, exp[expRel[k]], v)
446-
if k == 1 {
447-
fc.CustomFilters.And = andRel[exp[k]]
448-
}
442+
return
443+
}
444+
// Non default custom filter.
445+
expRel, andRel := map[int]int{0: 0, 1: 2}, map[int]bool{0: true, 1: false}
446+
for k, v := range tokens {
447+
f.writeCustomFilter(fc, exp[expRel[k]], v)
448+
if k == 1 {
449+
fc.CustomFilters.And = andRel[exp[k]]
449450
}
450451
}
451452
}
@@ -467,11 +468,11 @@ func (f *File) writeCustomFilter(fc *xlsxFilterColumn, operator int, val string)
467468
}
468469
if fc.CustomFilters != nil {
469470
fc.CustomFilters.CustomFilter = append(fc.CustomFilters.CustomFilter, &customFilter)
470-
} else {
471-
var customFilters []*xlsxCustomFilter
472-
customFilters = append(customFilters, &customFilter)
473-
fc.CustomFilters = &xlsxCustomFilters{CustomFilter: customFilters}
471+
return
474472
}
473+
var customFilters []*xlsxCustomFilter
474+
customFilters = append(customFilters, &customFilter)
475+
fc.CustomFilters = &xlsxCustomFilters{CustomFilter: customFilters}
475476
}
476477

477478
// parseFilterExpression provides a function to converts the tokens of a
@@ -488,8 +489,7 @@ func (f *File) parseFilterExpression(expression string, tokens []string) ([]int,
488489
if len(tokens) == 7 {
489490
// The number of tokens will be either 3 (for 1 expression) or 7 (for 2
490491
// expressions).
491-
conditional := 0
492-
c := tokens[3]
492+
conditional, c := 0, tokens[3]
493493
if conditionFormat.MatchString(c) {
494494
conditional = 1
495495
}
@@ -501,17 +501,13 @@ func (f *File) parseFilterExpression(expression string, tokens []string) ([]int,
501501
if err != nil {
502502
return expressions, t, err
503503
}
504-
expressions = []int{expression1[0], conditional, expression2[0]}
505-
t = []string{token1, token2}
506-
} else {
507-
exp, token, err := f.parseFilterTokens(expression, tokens)
508-
if err != nil {
509-
return expressions, t, err
510-
}
511-
expressions = exp
512-
t = []string{token}
504+
return []int{expression1[0], conditional, expression2[0]}, []string{token1, token2}, nil
505+
}
506+
exp, token, err := f.parseFilterTokens(expression, tokens)
507+
if err != nil {
508+
return expressions, t, err
513509
}
514-
return expressions, t, nil
510+
return exp, []string{token}, nil
515511
}
516512

517513
// parseFilterTokens provides a function to parse the 3 tokens of a filter
@@ -534,7 +530,7 @@ func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, str
534530
operator, ok := operators[strings.ToLower(tokens[1])]
535531
if !ok {
536532
// Convert the operator from a number to a descriptive string.
537-
return []int{}, "", fmt.Errorf("unknown operator: %s", tokens[1])
533+
return []int{}, "", newUnknownFilterTokenError(tokens[1])
538534
}
539535
token := tokens[2]
540536
// Special handling for Blanks/NonBlanks.
@@ -563,8 +559,7 @@ func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, str
563559
}
564560
// If the string token contains an Excel match character then change the
565561
// operator type to indicate a non "simple" equality.
566-
re = matchFormat.MatchString(token)
567-
if operator == 2 && re {
562+
if re = matchFormat.MatchString(token); operator == 2 && re {
568563
operator = 22
569564
}
570565
return []int{operator}, token, nil

0 commit comments

Comments
 (0)