Skip to content

Commit 8d996ca

Browse files
committed
This closes #1582, fixes the formula calculation bug, and improves form controls
- Fix incorrect formula calculate results on a nested argument function which returns a numeric result - Add a new exported error variable `ErrorFormControlValue` - Rename exported enumeration `FormControlCheckbox` to `FormControlCheckBox` - Rename exported enumeration `FormControlRadio` to `FormControlOptionButton` - The `AddFormControl` function supports new 5 form controls: spin button, check box, group box, label, and scroll bar - Update documentation for the `GraphicOptions` data type, `AddFormControl` and `NewStreamWriter` functions - Update the unit tests
1 parent b667987 commit 8d996ca

File tree

10 files changed

+417
-199
lines changed

10 files changed

+417
-199
lines changed

calc.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,16 +1053,16 @@ func (f *File) evalInfixExpFunc(ctx *calcContext, sheet, cell string, token, nex
10531053
if nextToken.TType == efp.TokenTypeOperatorInfix || (opftStack.Len() > 1 && opfdStack.Len() > 0) {
10541054
// mathematics calculate in formula function
10551055
opfdStack.Push(arg)
1056-
} else {
1057-
argsStack.Peek().(*list.List).PushBack(arg)
1058-
}
1059-
} else {
1060-
val := arg.Value()
1061-
if arg.Type == ArgMatrix && len(arg.Matrix) > 0 && len(arg.Matrix[0]) > 0 {
1062-
val = arg.Matrix[0][0].Value()
1056+
return newEmptyFormulaArg()
10631057
}
1064-
opdStack.Push(newStringFormulaArg(val))
1058+
argsStack.Peek().(*list.List).PushBack(arg)
1059+
return newEmptyFormulaArg()
1060+
}
1061+
if arg.Type == ArgMatrix && len(arg.Matrix) > 0 && len(arg.Matrix[0]) > 0 {
1062+
opdStack.Push(arg.Matrix[0][0])
1063+
return newEmptyFormulaArg()
10651064
}
1065+
opdStack.Push(arg)
10661066
return newEmptyFormulaArg()
10671067
}
10681068

calc_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5918,6 +5918,23 @@ func TestCalcCellResolver(t *testing.T) {
59185918
assert.NoError(t, err, formula)
59195919
assert.Equal(t, expected, result, formula)
59205920
}
5921+
// Test calculates formula that contains a nested argument function which returns a numeric result
5922+
f = NewFile()
5923+
for _, cell := range []string{"A1", "B2", "B3", "B4"} {
5924+
assert.NoError(t, f.SetCellValue("Sheet1", cell, "ABC"))
5925+
}
5926+
for cell, formula := range map[string]string{
5927+
"A2": "IF(B2<>\"\",MAX(A1:A1)+1,\"\")",
5928+
"A3": "IF(B3<>\"\",MAX(A1:A2)+1,\"\")",
5929+
"A4": "IF(B4<>\"\",MAX(A1:A3)+1,\"\")",
5930+
} {
5931+
assert.NoError(t, f.SetCellFormula("Sheet1", cell, formula))
5932+
}
5933+
for cell, expected := range map[string]string{"A2": "1", "A3": "2", "A4": "3"} {
5934+
result, err := f.CalcCellValue("Sheet1", cell)
5935+
assert.NoError(t, err)
5936+
assert.Equal(t, expected, result)
5937+
}
59215938
}
59225939

59235940
func TestEvalInfixExp(t *testing.T) {

errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,7 @@ var (
255255
// ErrUnprotectWorkbookPassword defined the error message on remove workbook
256256
// protection with password verification failed.
257257
ErrUnprotectWorkbookPassword = errors.New("workbook protect password not match")
258+
// ErrorFormControlValue defined the error message for receiving a scroll
259+
// value exceeds limit.
260+
ErrorFormControlValue = fmt.Errorf("scroll value must be between 0 and %d", MaxFormControlValue)
258261
)

picture.go

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -110,41 +110,46 @@ func parseGraphicOptions(opts *GraphicOptions) *GraphicOptions {
110110
// }
111111
// }
112112
//
113-
// The optional parameter "AutoFit" specifies if you make image size auto-fits the
114-
// cell, the default value of that is 'false'.
113+
// The optional parameter "AltText" is used to add alternative text to a graph
114+
// object.
115115
//
116-
// The optional parameter "Hyperlink" specifies the hyperlink of the image.
116+
// The optional parameter "PrintObject" indicates whether the graph object is
117+
// printed when the worksheet is printed, the default value of that is 'true'.
117118
//
118-
// The optional parameter "HyperlinkType" defines two types of
119-
// hyperlink "External" for website or "Location" for moving to one of the
120-
// cells in this workbook. When the "HyperlinkType" is "Location",
121-
// coordinates need to start with "#".
119+
// The optional parameter "Locked" indicates whether lock the graph object.
120+
// Locking an object has no effect unless the sheet is protected.
122121
//
123-
// The optional parameter "Positioning" defines two types of the position of an
124-
// image in an Excel spreadsheet, "oneCell" (Move but don't size with
125-
// cells) or "absolute" (Don't move or size with cells). If you don't set this
126-
// parameter, the default positioning is move and size with cells.
122+
// The optional parameter "LockAspectRatio" indicates whether lock aspect ratio
123+
// for the graph object, the default value of that is 'false'.
127124
//
128-
// The optional parameter "PrintObject" indicates whether the image is printed
129-
// when the worksheet is printed, the default value of that is 'true'.
125+
// The optional parameter "AutoFit" specifies if you make graph object size
126+
// auto-fits the cell, the default value of that is 'false'.
130127
//
131-
// The optional parameter "LockAspectRatio" indicates whether lock aspect
132-
// ratio for the image, the default value of that is 'false'.
128+
// The optional parameter "OffsetX" specifies the horizontal offset of the graph
129+
// object with the cell, the default value of that is 0.
133130
//
134-
// The optional parameter "Locked" indicates whether lock the image. Locking
135-
// an object has no effect unless the sheet is protected.
131+
// The optional parameter "OffsetY" specifies the vertical offset of the graph
132+
// object with the cell, the default value of that is 0.
136133
//
137-
// The optional parameter "OffsetX" specifies the horizontal offset of the
138-
// image with the cell, the default value of that is 0.
134+
// The optional parameter "ScaleX" specifies the horizontal scale of graph
135+
// object, the default value of that is 1.0 which presents 100%.
139136
//
140-
// The optional parameter "ScaleX" specifies the horizontal scale of images,
137+
// The optional parameter "ScaleY" specifies the vertical scale of graph object,
141138
// the default value of that is 1.0 which presents 100%.
142139
//
143-
// The optional parameter "OffsetY" specifies the vertical offset of the
144-
// image with the cell, the default value of that is 0.
140+
// The optional parameter "Hyperlink" specifies the hyperlink of the graph
141+
// object.
145142
//
146-
// The optional parameter "ScaleY" specifies the vertical scale of images,
147-
// the default value of that is 1.0 which presents 100%.
143+
// The optional parameter "HyperlinkType" defines two types of
144+
// hyperlink "External" for website or "Location" for moving to one of the
145+
// cells in this workbook. When the "HyperlinkType" is "Location",
146+
// coordinates need to start with "#".
147+
//
148+
// The optional parameter "Positioning" defines 3 types of the position of a
149+
// graph object in a spreadsheet: "oneCell" (Move but don't size with
150+
// cells), "twoCell" (Move and size with cells), and "absolute" (Don't move or
151+
// size with cells). If you don't set this parameter, the default positioning
152+
// is to move and size with cells.
148153
func (f *File) AddPicture(sheet, cell, name string, opts *GraphicOptions) error {
149154
var err error
150155
// Check picture exists first.
@@ -330,6 +335,9 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, ext string, rID, hyper
330335
if err != nil {
331336
return err
332337
}
338+
if opts.Positioning != "" && inStrSlice(supportedPositioning, opts.Positioning, true) == -1 {
339+
return ErrParameterInvalid
340+
}
333341
width, height := img.Width, img.Height
334342
if opts.AutoFit {
335343
if width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), opts); err != nil {

picture_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ func TestAddDrawingPicture(t *testing.T) {
192192
f := NewFile()
193193
opts := &GraphicOptions{PrintObject: boolPtr(true), Locked: boolPtr(false)}
194194
assert.EqualError(t, f.addDrawingPicture("sheet1", "", "A", "", 0, 0, image.Config{}, opts), newCellNameToCoordinatesError("A", newInvalidCellNameError("A")).Error())
195+
// Test addDrawingPicture with invalid positioning types
196+
assert.Equal(t, f.addDrawingPicture("sheet1", "", "A1", "", 0, 0, image.Config{}, &GraphicOptions{Positioning: "x"}), ErrParameterInvalid)
195197

196198
path := "xl/drawings/drawing1.xml"
197199
f.Pkg.Store(path, MacintoshCyrillicCharset)

stream.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,16 @@ type StreamWriter struct {
3838
tableParts string
3939
}
4040

41-
// NewStreamWriter return stream writer struct by given worksheet name for
42-
// generate new worksheet with large amounts of data. Note that after set
43-
// rows, you must call the 'Flush' method to end the streaming writing process
44-
// and ensure that the order of row numbers is ascending, the normal mode
45-
// functions and stream mode functions can't be work mixed to writing data on
46-
// the worksheets, you can't get cell value when in-memory chunks data over
47-
// 16MB. For example, set data for worksheet of size 102400 rows x 50 columns
48-
// with numbers and style:
41+
// NewStreamWriter returns stream writer struct by given worksheet name used for
42+
// writing data on a new existing empty worksheet with large amounts of data.
43+
// Note that after writing data with the stream writer for the worksheet, you
44+
// must call the 'Flush' method to end the streaming writing process, ensure
45+
// that the order of row numbers is ascending when set rows, and the normal
46+
// mode functions and stream mode functions can not be work mixed to writing
47+
// data on the worksheets. The stream writer will try to use temporary files on
48+
// disk to reduce the memory usage when in-memory chunks data over 16MB, and
49+
// you can't get cell value at this time. For example, set data for worksheet
50+
// of size 102400 rows x 50 columns with numbers and style:
4951
//
5052
// f := excelize.NewFile()
5153
// defer func() {

0 commit comments

Comments
 (0)