mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +08:00
* Issue #376 fix - RemoveColumn * Removing of columns is forbidden when there are formula arrays in the area of removing, except 1-column wide arrays * Modifying named ranges, column ranges when deleting a column * Updating formulas when deleting a column * UpdateAction
This commit is contained in:
parent
280d692114
commit
ddafaca850
38
_examples/spreadsheet/remove-column/main.go
Normal file
38
_examples/spreadsheet/remove-column/main.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2017 FoxyUtils ehf. All rights reserved.
|
||||
package main
|
||||
// This example demonstrates flattening all formulas from an input Excel file and outputs the flattened values to a new xlsx.
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ss, err := spreadsheet.Open("original.xlsx")
|
||||
if err != nil {
|
||||
log.Fatalf("error opening document: %s", err)
|
||||
}
|
||||
|
||||
sheet0, err := ss.GetSheet("Cells")
|
||||
if err != nil {
|
||||
log.Fatalf("error opening sheet: %s", err)
|
||||
}
|
||||
|
||||
err = sheet0.RemoveColumn("D")
|
||||
if err != nil {
|
||||
log.Fatalf("error removing column: %s", err)
|
||||
}
|
||||
|
||||
sheet1, err := ss.GetSheet("MergedCells")
|
||||
if err != nil {
|
||||
log.Fatalf("error opening sheet: %s", err)
|
||||
}
|
||||
|
||||
err = sheet1.RemoveColumn("C")
|
||||
if err != nil {
|
||||
log.Fatalf("error removing column: %s", err)
|
||||
}
|
||||
|
||||
ss.SaveToFile("removed.xlsx")
|
||||
}
|
BIN
_examples/spreadsheet/remove-column/original.xlsx
Normal file
BIN
_examples/spreadsheet/remove-column/original.xlsx
Normal file
Binary file not shown.
@ -10,6 +10,8 @@ package formula
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// BinOpType is the binary operation operator type
|
||||
@ -403,3 +405,43 @@ func listValueOp(op BinOpType, lhs []Result, rhs Result) Result {
|
||||
}
|
||||
return MakeListResult(res)
|
||||
}
|
||||
|
||||
// Eval evaluates the binary expression using the context given.
|
||||
func (b BinaryExpr) String() string {
|
||||
opStr := ""
|
||||
switch b.op {
|
||||
case BinOpTypePlus:
|
||||
opStr = "+"
|
||||
case BinOpTypeMinus:
|
||||
opStr = "-"
|
||||
case BinOpTypeMult:
|
||||
opStr = "*"
|
||||
case BinOpTypeDiv:
|
||||
opStr = "/"
|
||||
case BinOpTypeExp:
|
||||
opStr = "^"
|
||||
case BinOpTypeLT:
|
||||
opStr = "<"
|
||||
case BinOpTypeGT:
|
||||
opStr = ">"
|
||||
case BinOpTypeEQ:
|
||||
opStr = "="
|
||||
case BinOpTypeLEQ:
|
||||
opStr = "<="
|
||||
case BinOpTypeGEQ:
|
||||
opStr = ">="
|
||||
case BinOpTypeNE:
|
||||
opStr = "<>"
|
||||
case BinOpTypeConcat:
|
||||
opStr = "&"
|
||||
}
|
||||
return b.lhs.String() + opStr + b.rhs.String()
|
||||
}
|
||||
|
||||
// Update updates references in the BinaryExpr after removing a row/column.
|
||||
func (b BinaryExpr) Update(q *update.UpdateQuery) Expression {
|
||||
new := b
|
||||
new.lhs = b.lhs.Update(q)
|
||||
new.rhs = b.rhs.Update(q)
|
||||
return new
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/unidoc/unioffice"
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// Bool is a boolean expression.
|
||||
@ -36,3 +37,17 @@ func (b Bool) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (b Bool) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation for Bool.
|
||||
func (b Bool) String() string {
|
||||
if b.b {
|
||||
return "TRUE"
|
||||
} else {
|
||||
return "FALSE"
|
||||
}
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect Bool.
|
||||
func (b Bool) Update(q *update.UpdateQuery) Expression {
|
||||
return b
|
||||
}
|
||||
|
@ -7,6 +7,11 @@
|
||||
|
||||
package formula
|
||||
|
||||
import (
|
||||
"github.com/unidoc/unioffice/spreadsheet/reference"
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// CellRef is a reference to a single cell
|
||||
type CellRef struct {
|
||||
s string
|
||||
@ -26,3 +31,36 @@ func (c CellRef) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (c CellRef) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return Reference{Type: ReferenceTypeCell, Value: c.s}
|
||||
}
|
||||
|
||||
// String returns a string representation of CellRef.
|
||||
func (c CellRef) String() string {
|
||||
return c.s
|
||||
}
|
||||
|
||||
// Update makes a reference to point to one of the neighboring cells after removing a row/column with respect to the update type.
|
||||
func (c CellRef) Update(q *update.UpdateQuery) Expression {
|
||||
if q.UpdateCurrentSheet {
|
||||
c.s = updateRefStr(c.s, q)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// updateRefStr gets reference string representation like C1, parses it and makes a string representation of a new reference with respect to the update type (e.g. B1 if a column to the left of this reference was removed).
|
||||
func updateRefStr(refStr string, q *update.UpdateQuery) string {
|
||||
ref, err := reference.ParseCellReference(refStr)
|
||||
if err != nil {
|
||||
return "#REF!"
|
||||
}
|
||||
if q.UpdateType == update.UpdateActionRemoveColumn {
|
||||
columnIdxToRemove := q.ColumnIdx
|
||||
columnIdx := ref.ColumnIdx
|
||||
if columnIdx < columnIdxToRemove {
|
||||
return refStr
|
||||
} else if columnIdx == columnIdxToRemove {
|
||||
return "#REF!"
|
||||
} else {
|
||||
return ref.Update(update.UpdateActionRemoveColumn).String()
|
||||
}
|
||||
}
|
||||
return refStr
|
||||
}
|
||||
|
22
spreadsheet/formula/columnref.go
Normal file
22
spreadsheet/formula/columnref.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2017 FoxyUtils ehf. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by the terms of the Affero GNU General
|
||||
// Public License version 3.0 as published by the Free Software Foundation and
|
||||
// appearing in the file LICENSE included in the packaging of this file. A
|
||||
// commercial license can be purchased on https://unidoc.io.
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/reference"
|
||||
|
||||
// updateColumnToLeft gets a column reference string representation like JJ, parses it and makes a string representation of a new reference with respect to the update type in the case of a column to the left of this reference was removed (e.g. JI).
|
||||
func updateColumnToLeft(column string, colIdxToRemove uint32) string {
|
||||
colIdx := reference.ColumnToIndex(column)
|
||||
if colIdx == colIdxToRemove {
|
||||
return "#REF!"
|
||||
} else if colIdx > colIdxToRemove {
|
||||
return reference.IndexToColumn(colIdx - 1)
|
||||
} else {
|
||||
return column
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
// ConstArrayExpr is a constant array expression.
|
||||
type ConstArrayExpr struct {
|
||||
data [][]Expression
|
||||
@ -34,3 +36,13 @@ func (c ConstArrayExpr) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (c ConstArrayExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation of ConstArrayExpr.
|
||||
func (c ConstArrayExpr) String() string {
|
||||
return "" // to do
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect ConstArrayExpr.
|
||||
func (c ConstArrayExpr) Update(q *update.UpdateQuery) Expression {
|
||||
return c
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
// EmptyExpr is an empty expression.
|
||||
type EmptyExpr struct {
|
||||
}
|
||||
@ -25,3 +27,13 @@ func (e EmptyExpr) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (e EmptyExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns an empty string for EmptyExpr.
|
||||
func (e EmptyExpr) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect EmptyExpr.
|
||||
func (e EmptyExpr) Update(q *update.UpdateQuery) Expression {
|
||||
return e
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
// Error is an error expression.
|
||||
type Error struct {
|
||||
s string
|
||||
@ -26,3 +28,13 @@ func (e Error) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (e Error) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns an empty string for Error.
|
||||
func (e Error) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect Error.
|
||||
func (e Error) Update(q *update.UpdateQuery) Expression {
|
||||
return e
|
||||
}
|
||||
|
@ -7,7 +7,11 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
type Expression interface {
|
||||
Eval(ctx Context, ev Evaluator) Result
|
||||
Reference(ctx Context, ev Evaluator) Reference
|
||||
String() string
|
||||
Update(updateQuery *update.UpdateQuery) Expression
|
||||
}
|
||||
|
@ -7,6 +7,11 @@
|
||||
|
||||
package formula
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// FunctionCall is a function call expression.
|
||||
type FunctionCall struct {
|
||||
name string
|
||||
@ -46,3 +51,32 @@ func (f FunctionCall) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (f FunctionCall) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation of FunctionCall expression.
|
||||
func (f FunctionCall) String() string {
|
||||
buf := bytes.Buffer{}
|
||||
buf.WriteString(f.name)
|
||||
buf.WriteString("(")
|
||||
lastArgIndex := len(f.args) - 1
|
||||
for argIndex, arg := range f.args {
|
||||
buf.WriteString(arg.String())
|
||||
if argIndex != lastArgIndex {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
buf.WriteString(")")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Update updates the FunctionCall references after removing a row/column.
|
||||
func (f FunctionCall) Update(q *update.UpdateQuery) Expression {
|
||||
newArgs := []Expression{}
|
||||
for _, arg := range f.args {
|
||||
newArg := arg.Update(q)
|
||||
newArgs = append(newArgs, newArg)
|
||||
}
|
||||
return FunctionCall{
|
||||
name: f.name,
|
||||
args: newArgs,
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
@ -56,3 +58,13 @@ func cellRefsFromHorizontalRange(ctx Context, rowFrom, rowTo int) (string, strin
|
||||
to := lastColumn + strconv.Itoa(rowTo)
|
||||
return from, to
|
||||
}
|
||||
|
||||
// String returns a string representation of a horizontal range.
|
||||
func (r HorizontalRange) String() string {
|
||||
return r.horizontalRangeReference()
|
||||
}
|
||||
|
||||
// Update updates the horizontal range references after removing a row/column.
|
||||
func (r HorizontalRange) Update(q *update.UpdateQuery) Expression {
|
||||
return r
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ package formula
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// NamedRangeRef is a reference to a named range.
|
||||
@ -53,3 +55,13 @@ func (n NamedRangeRef) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (n NamedRangeRef) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return Reference{Type: ReferenceTypeNamedRange, Value: n.s}
|
||||
}
|
||||
|
||||
// String returns a string representation of a named range.
|
||||
func (n NamedRangeRef) String() string {
|
||||
return n.s
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect named ranges.
|
||||
func (n NamedRangeRef) Update(q *update.UpdateQuery) Expression {
|
||||
return n
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
// Negate is a negate expression like -A1.
|
||||
type Negate struct {
|
||||
e Expression
|
||||
@ -30,3 +32,13 @@ func (n Negate) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (n Negate) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation for Negate.
|
||||
func (n Negate) String() string {
|
||||
return "-" + n.e.String()
|
||||
}
|
||||
|
||||
// Update updates references in the Negate after removing a row/column.
|
||||
func (n Negate) Update(q *update.UpdateQuery) Expression {
|
||||
return Negate{n.e.Update(q)}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/unidoc/unioffice"
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// Number is a nubmer expression.
|
||||
@ -36,3 +37,13 @@ func (n Number) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (n Number) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation of Number.
|
||||
func (n Number) String() string {
|
||||
return strconv.FormatFloat(n.v, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect Number.
|
||||
func (n Number) Update(q *update.UpdateQuery) Expression {
|
||||
return n
|
||||
}
|
||||
|
@ -7,7 +7,11 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// PrefixExpr is an expression containing reference to another sheet like Sheet1!A1 (the value of the cell A1 from sheet 'Sheet1').
|
||||
type PrefixExpr struct {
|
||||
@ -41,3 +45,20 @@ func (p PrefixExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||
}
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation of PrefixExpr.
|
||||
func (p PrefixExpr) String() string {
|
||||
return fmt.Sprintf("%s!%s", p.pfx.String(), p.exp.String())
|
||||
}
|
||||
|
||||
// Update updates references in the PrefixExpr after removing a row/column.
|
||||
func (p PrefixExpr) Update(q *update.UpdateQuery) Expression {
|
||||
new := p
|
||||
sheetName := p.pfx.String()
|
||||
if sheetName == q.SheetToUpdate {
|
||||
newQ := *q
|
||||
newQ.UpdateCurrentSheet = true
|
||||
new.exp = p.exp.Update(&newQ)
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// PrefixHorizontalRange is a range expression that when evaluated returns a list of Results from references like Sheet1!1:4 (all cells from rows 1 to 4 of sheet 'Sheet1').
|
||||
@ -58,3 +60,13 @@ func (r PrefixHorizontalRange) Reference(ctx Context, ev Evaluator) Reference {
|
||||
pfx := r.pfx.Reference(ctx, ev)
|
||||
return Reference{Type: ReferenceTypeHorizontalRange, Value: r.horizontalRangeReference(pfx.Value)}
|
||||
}
|
||||
|
||||
// String returns a string representation of a horizontal range with prefix.
|
||||
func (r PrefixHorizontalRange) String() string {
|
||||
return fmt.Sprintf("%s!%d:%d", r.pfx.String(), r.rowFrom, r.rowTo)
|
||||
}
|
||||
|
||||
// Update updates references in the PrefixHorizontalRange after removing a row/column.
|
||||
func (r PrefixHorizontalRange) Update(q *update.UpdateQuery) Expression {
|
||||
return r
|
||||
}
|
||||
|
@ -7,7 +7,11 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// PrefixRangeExpr is a range expression that when evaluated returns a list of Results from a given sheet like Sheet1!A1:B4 (all cells from A1 to B4 from a sheet 'Sheet1').
|
||||
type PrefixRangeExpr struct {
|
||||
@ -56,3 +60,21 @@ func (p PrefixRangeExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||
}
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation of a range with prefix.
|
||||
func (r PrefixRangeExpr) String() string {
|
||||
return fmt.Sprintf("%s!%s:%s", r.pfx.String(), r.from.String(), r.to.String())
|
||||
}
|
||||
|
||||
// Update updates references in the PrefixRangeExpr after removing a row/column.
|
||||
func (r PrefixRangeExpr) Update(q *update.UpdateQuery) Expression {
|
||||
new := r
|
||||
sheetName := r.pfx.String()
|
||||
if sheetName == q.SheetToUpdate {
|
||||
newQ := *q
|
||||
newQ.UpdateCurrentSheet = true
|
||||
new.from = r.from.Update(&newQ)
|
||||
new.to = r.to.Update(&newQ)
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ package formula
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// PrefixVerticalRange is a range expression that when evaluated returns a list of Results from references like Sheet1!AA:IJ (all cells from columns AA to IJ of sheet 'Sheet1').
|
||||
@ -55,3 +57,23 @@ func (r PrefixVerticalRange) Reference(ctx Context, ev Evaluator) Reference {
|
||||
pfx := r.pfx.Reference(ctx, ev)
|
||||
return Reference{Type: ReferenceTypeVerticalRange, Value: r.verticalRangeReference(pfx.Value)}
|
||||
}
|
||||
|
||||
// String returns a string representation of a vertical range with prefix.
|
||||
func (r PrefixVerticalRange) String() string {
|
||||
return fmt.Sprintf("%s!%s:%s", r.pfx.String(), r.colFrom, r.colTo)
|
||||
}
|
||||
|
||||
// Update updates references in the PrefixVerticalRange after removing a row/column.
|
||||
func (r PrefixVerticalRange) Update(q *update.UpdateQuery) Expression {
|
||||
if q.UpdateType == update.UpdateActionRemoveColumn {
|
||||
new := r
|
||||
sheetName := r.pfx.String()
|
||||
if sheetName == q.SheetToUpdate {
|
||||
columnIdx := q.ColumnIdx
|
||||
new.colFrom = updateColumnToLeft(r.colFrom, columnIdx)
|
||||
new.colTo = updateColumnToLeft(r.colTo, columnIdx)
|
||||
}
|
||||
return new
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/reference"
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// Range is a range expression that when evaluated returns a list of Results.
|
||||
@ -87,3 +88,18 @@ func resultFromCellRange(ctx Context, ev Evaluator, from, to string) Result {
|
||||
|
||||
return MakeArrayResult(arr)
|
||||
}
|
||||
|
||||
// String returns a string of a range.
|
||||
func (r Range) String() string {
|
||||
return fmt.Sprintf("%s:%s", r.from.String(), r.to.String())
|
||||
}
|
||||
|
||||
// Update updates references in the Range after removing a row/column.
|
||||
func (r Range) Update(q *update.UpdateQuery) Expression {
|
||||
new := r
|
||||
if q.UpdateCurrentSheet {
|
||||
new.from = r.from.Update(q)
|
||||
new.to = r.to.Update(q)
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
// SheetPrefixExpr is a reference to a sheet like Sheet1! (reference to sheet 'Sheet1').
|
||||
type SheetPrefixExpr struct {
|
||||
sheet string
|
||||
@ -26,3 +28,13 @@ func (s SheetPrefixExpr) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (s SheetPrefixExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return Reference{Type: ReferenceTypeSheet, Value: s.sheet}
|
||||
}
|
||||
|
||||
// String returns a string representation of SheetPrefixExpr.
|
||||
func (s SheetPrefixExpr) String() string {
|
||||
return s.sheet
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect SheetPrefixExpr.
|
||||
func (s SheetPrefixExpr) Update(q *update.UpdateQuery) Expression {
|
||||
return s
|
||||
}
|
||||
|
@ -7,7 +7,11 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// String is a string expression.
|
||||
type String struct {
|
||||
@ -30,3 +34,13 @@ func (s String) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (s String) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// String returns a string representation of String.
|
||||
func (s String) String() string {
|
||||
return `"` + s.s + `"`
|
||||
}
|
||||
|
||||
// Update returns the same object as updating sheet references does not affect String.
|
||||
func (s String) Update(q *update.UpdateQuery) Expression {
|
||||
return s
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// VerticalRange is a range expression that when evaluated returns a list of Results from references like AA:IJ (all cells from columns AA to IJ).
|
||||
@ -54,3 +56,22 @@ func cellRefsFromVerticalRange(ctx Context, colFrom, colTo string) (string, stri
|
||||
to := colTo + strconv.Itoa(lastRow)
|
||||
return from, to
|
||||
}
|
||||
|
||||
// String returns a string representation of a vertical range.
|
||||
func (r VerticalRange) String() string {
|
||||
return r.verticalRangeReference()
|
||||
}
|
||||
|
||||
// Update updates references in the VerticalRange after removing a row/column.
|
||||
func (r VerticalRange) Update(q *update.UpdateQuery) Expression {
|
||||
if q.UpdateType == update.UpdateActionRemoveColumn {
|
||||
new := r
|
||||
if q.UpdateCurrentSheet {
|
||||
columnIdx := q.ColumnIdx
|
||||
new.colFrom = updateColumnToLeft(r.colFrom, columnIdx)
|
||||
new.colTo = updateColumnToLeft(r.colTo, columnIdx)
|
||||
}
|
||||
return new
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// CellReference is a parsed reference to a cell. Input is of the form 'A1',
|
||||
@ -25,6 +27,7 @@ type CellReference struct {
|
||||
SheetName string
|
||||
}
|
||||
|
||||
// String returns a string representation of CellReference.
|
||||
func (c CellReference) String() string {
|
||||
buf := make([]byte, 0, 4)
|
||||
if c.AbsoluteColumn {
|
||||
@ -90,3 +93,16 @@ lfor:
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Update updates reference to point one of the neighboring cells with respect to the update type after removing a row/column.
|
||||
func (ref *CellReference) Update(updateType update.UpdateAction) *CellReference {
|
||||
switch updateType {
|
||||
case update.UpdateActionRemoveColumn:
|
||||
newRef := ref
|
||||
newRef.ColumnIdx = ref.ColumnIdx - 1
|
||||
newRef.Column = IndexToColumn(newRef.ColumnIdx)
|
||||
return newRef
|
||||
default:
|
||||
return ref
|
||||
}
|
||||
}
|
||||
|
80
spreadsheet/reference/columnreference.go
Normal file
80
spreadsheet/reference/columnreference.go
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2017 FoxyUtils ehf. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by the terms of the Affero GNU General
|
||||
// Public License version 3.0 as published by the Free Software Foundation and
|
||||
// appearing in the file LICENSE included in the packaging of this file. A
|
||||
// commercial license can be purchased on https://unidoc.io.
|
||||
|
||||
package reference
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
// ColumnReference is a parsed reference to a column. Input is of the form 'A',
|
||||
// '$C', etc.
|
||||
type ColumnReference struct {
|
||||
ColumnIdx uint32
|
||||
Column string
|
||||
AbsoluteColumn bool
|
||||
SheetName string
|
||||
}
|
||||
|
||||
// String returns a string representation of ColumnReference.
|
||||
func (c ColumnReference) String() string {
|
||||
buf := make([]byte, 0, 4)
|
||||
if c.AbsoluteColumn {
|
||||
buf = append(buf, '$')
|
||||
}
|
||||
buf = append(buf, c.Column...)
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
var reColumn = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z]?)$`)
|
||||
|
||||
// ParseColumnReference parses a column reference of the form 'Sheet1!A' and splits it
|
||||
// into sheet name and column segments.
|
||||
func ParseColumnReference(s string) (ColumnReference, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if len(s) < 1 {
|
||||
return ColumnReference{}, errors.New("column reference must have at least one character")
|
||||
}
|
||||
|
||||
r := ColumnReference{}
|
||||
sl := strings.Split(s, "!")
|
||||
if len(sl) == 2 {
|
||||
r.SheetName = sl[0]
|
||||
s = sl[1]
|
||||
}
|
||||
// check for absolute column
|
||||
if s[0] == '$' {
|
||||
r.AbsoluteColumn = true
|
||||
s = s[1:]
|
||||
}
|
||||
|
||||
if !reColumn.MatchString(s) {
|
||||
return ColumnReference{}, errors.New("column reference must be between A and ZZ")
|
||||
}
|
||||
|
||||
r.Column = s
|
||||
|
||||
r.ColumnIdx = ColumnToIndex(r.Column)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Update updates reference to point one of the neighboring columns with respect to the update type after removing a row/column.
|
||||
func (ref *ColumnReference) Update(updateType update.UpdateAction) *ColumnReference {
|
||||
switch updateType {
|
||||
case update.UpdateActionRemoveColumn:
|
||||
newRef := ref
|
||||
newRef.ColumnIdx = ref.ColumnIdx - 1
|
||||
newRef.Column = IndexToColumn(newRef.ColumnIdx)
|
||||
return newRef
|
||||
default:
|
||||
return ref
|
||||
}
|
||||
}
|
@ -41,3 +41,33 @@ func ParseRangeReference(s string) (from, to CellReference, err error) {
|
||||
}
|
||||
return fromRef, toRef, nil
|
||||
}
|
||||
|
||||
// ParseColumnRangeReference splits a range reference of the form "A:B" into its
|
||||
// components.
|
||||
func ParseColumnRangeReference(s string) (from, to ColumnReference, err error) {
|
||||
sheetName := ""
|
||||
sp0 := strings.Split(s, "!")
|
||||
if len(sp0) == 2 {
|
||||
sheetName = sp0[0]
|
||||
s = sp0[1]
|
||||
}
|
||||
sp := strings.Split(s, ":")
|
||||
if len(sp) != 2 {
|
||||
return ColumnReference{}, ColumnReference{}, errors.New("invalid range format")
|
||||
}
|
||||
|
||||
if sheetName != "" {
|
||||
sp[0] = sheetName + "!" + sp[0]
|
||||
sp[1] = sheetName + "!" + sp[1]
|
||||
}
|
||||
fromRef, err := ParseColumnReference(sp[0])
|
||||
if err != nil {
|
||||
return ColumnReference{}, ColumnReference{}, err
|
||||
}
|
||||
|
||||
toRef, err := ParseColumnReference(sp[1])
|
||||
if err != nil {
|
||||
return ColumnReference{}, ColumnReference{}, err
|
||||
}
|
||||
return fromRef, toRef, nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
package spreadsheet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/formula"
|
||||
"github.com/unidoc/unioffice/spreadsheet/reference"
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
|
||||
"github.com/unidoc/unioffice"
|
||||
"github.com/unidoc/unioffice/common"
|
||||
@ -871,3 +873,257 @@ func (s *Sheet) Sort(column string, firstRow uint32, order SortOrder) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveColumn removes column from the sheet and moves all columns to the right of the removed column one step left.
|
||||
func (s *Sheet) RemoveColumn(column string) error {
|
||||
cellsInFormulaArrays, err := s.getAllCellsInFormulaArraysForColumn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
columnIdx := reference.ColumnToIndex(column)
|
||||
for _, row := range s.Rows() {
|
||||
ref := fmt.Sprintf("%s%d", column, *row.X().RAttr)
|
||||
if _, ok := cellsInFormulaArrays[ref]; ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for _, row := range s.Rows() {
|
||||
cells := row.x.C
|
||||
for ic, cell := range cells {
|
||||
ref, err := reference.ParseCellReference(*cell.RAttr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ref.ColumnIdx == columnIdx {
|
||||
row.x.C = append(cells[:ic], s.slideCellsLeft(cells[ic+1:])...)
|
||||
break
|
||||
} else if ref.ColumnIdx > columnIdx {
|
||||
row.x.C = append(cells[:ic], s.slideCellsLeft(cells[ic:])...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = s.updateAfterRemove(columnIdx, update.UpdateActionRemoveColumn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.removeColumnFromNamedRanges(columnIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.removeColumnFromMergedCells(columnIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sheet := range s.w.Sheets() {
|
||||
sheet.RecalculateFormulas()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sheet) updateAfterRemove(columnIdx uint32, updateType update.UpdateAction) error {
|
||||
ownSheetName := s.Name()
|
||||
q := &update.UpdateQuery{
|
||||
UpdateType: updateType,
|
||||
ColumnIdx: columnIdx,
|
||||
SheetToUpdate: ownSheetName,
|
||||
}
|
||||
for _, sheet := range s.w.Sheets() {
|
||||
q.UpdateCurrentSheet = ownSheetName == sheet.Name()
|
||||
for _, r := range sheet.Rows() {
|
||||
for _, c := range r.Cells() {
|
||||
if c.X().F != nil {
|
||||
formStr := c.X().F.Content
|
||||
expr := formula.ParseString(formStr)
|
||||
if expr == nil {
|
||||
c.SetError("#REF!")
|
||||
} else {
|
||||
newExpr := expr.Update(q)
|
||||
c.X().F.Content = fmt.Sprintf("=%s", newExpr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sheet) slideCellsLeft(cells []*sml.CT_Cell) []*sml.CT_Cell {
|
||||
for _, cell := range cells {
|
||||
ref, err := reference.ParseCellReference(*cell.RAttr)
|
||||
if err != nil {
|
||||
return cells
|
||||
}
|
||||
newColumnIdx := ref.ColumnIdx - 1
|
||||
newRefStr := reference.IndexToColumn(newColumnIdx) + fmt.Sprintf("%d", ref.RowIdx)
|
||||
cell.RAttr = &newRefStr
|
||||
}
|
||||
return cells
|
||||
}
|
||||
|
||||
func (s *Sheet) removeColumnFromMergedCells(columnIdx uint32) error {
|
||||
if s.x.MergeCells == nil || s.x.MergeCells.MergeCell == nil {
|
||||
return nil
|
||||
}
|
||||
newMergedCells := []*sml.CT_MergeCell{}
|
||||
for _, mc := range s.MergedCells() {
|
||||
newRefStr := moveRangeLeft(mc.Reference(), columnIdx, true)
|
||||
if newRefStr != "" {
|
||||
mc.SetReference(newRefStr)
|
||||
newMergedCells = append(newMergedCells, mc.X())
|
||||
}
|
||||
}
|
||||
s.x.MergeCells.MergeCell = newMergedCells
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sheet) removeColumnFromNamedRanges(columnIdx uint32) error {
|
||||
for _, dn := range s.w.DefinedNames() {
|
||||
name := dn.Name()
|
||||
content := dn.Content()
|
||||
sp := strings.Split(content, "!")
|
||||
if len(sp) != 2 {
|
||||
return errors.New("Incorrect named range:" + content)
|
||||
}
|
||||
sheetName := sp[0]
|
||||
if s.Name() == sheetName {
|
||||
err := s.w.RemoveDefinedName(dn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRefStr := moveRangeLeft(sp[1], columnIdx, true)
|
||||
if newRefStr != "" {
|
||||
newContent := sheetName + "!" + newRefStr
|
||||
s.w.AddDefinedName(name, newContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
numTables := 0
|
||||
if s.x.TableParts != nil && s.x.TableParts.TablePart != nil {
|
||||
numTables = len(s.x.TableParts.TablePart)
|
||||
}
|
||||
if numTables != 0 {
|
||||
startFromTable := 0
|
||||
for _, sheet := range s.w.Sheets() {
|
||||
if sheet.Name() == s.Name() {
|
||||
break
|
||||
} else {
|
||||
if sheet.x.TableParts != nil && sheet.x.TableParts.TablePart != nil {
|
||||
startFromTable += len(sheet.x.TableParts.TablePart)
|
||||
}
|
||||
}
|
||||
}
|
||||
sheetTables := s.w.tables[startFromTable:startFromTable + numTables]
|
||||
for tblIndex, tbl := range sheetTables {
|
||||
newTable := tbl
|
||||
newTable.RefAttr = moveRangeLeft(newTable.RefAttr, columnIdx, false)
|
||||
s.w.tables[startFromTable + tblIndex] = newTable
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func moveRangeLeft(ref string, columnIdx uint32, remove bool) string {
|
||||
fromCell, toCell, err := reference.ParseRangeReference(ref)
|
||||
if err == nil {
|
||||
fromColIdx, toColIdx := fromCell.ColumnIdx, toCell.ColumnIdx
|
||||
if columnIdx >= fromColIdx && columnIdx <= toColIdx {
|
||||
if fromColIdx == toColIdx {
|
||||
if remove {
|
||||
return ""
|
||||
} else {
|
||||
return ref
|
||||
}
|
||||
} else {
|
||||
newTo := toCell.Update(update.UpdateActionRemoveColumn)
|
||||
return fmt.Sprintf("%s:%s", fromCell.String(), newTo.String())
|
||||
}
|
||||
} else if columnIdx < fromColIdx {
|
||||
newFrom := fromCell.Update(update.UpdateActionRemoveColumn)
|
||||
newTo := toCell.Update(update.UpdateActionRemoveColumn)
|
||||
return fmt.Sprintf("%s:%s", newFrom.String(), newTo.String())
|
||||
}
|
||||
} else {
|
||||
fromColumn, toColumn, err := reference.ParseColumnRangeReference(ref)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
fromColIdx, toColIdx := fromColumn.ColumnIdx, toColumn.ColumnIdx
|
||||
if columnIdx >= fromColIdx && columnIdx <= toColIdx {
|
||||
if fromColIdx == toColIdx {
|
||||
if remove {
|
||||
return ""
|
||||
} else {
|
||||
return ref
|
||||
}
|
||||
} else {
|
||||
newTo := toColumn.Update(update.UpdateActionRemoveColumn)
|
||||
return fmt.Sprintf("%s:%s", fromColumn.String(), newTo.String())
|
||||
}
|
||||
} else if columnIdx < fromColIdx {
|
||||
newFrom := fromColumn.Update(update.UpdateActionRemoveColumn)
|
||||
newTo := toColumn.Update(update.UpdateActionRemoveColumn)
|
||||
return fmt.Sprintf("%s:%s", newFrom.String(), newTo.String())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Sheet) getAllCellsInFormulaArraysForColumn() (map[string]bool, error) {
|
||||
return s.getAllCellsInFormulaArrays(false)
|
||||
}
|
||||
|
||||
// getAllCellsInFormulaArrays returns all cells of the sheet that are covered by formula arrays. It is a helper for checking when removing rows and columns and skips all arrays of length 1 column when removing columns and all arrays of length 1 row when removing rows.
|
||||
func (s *Sheet) getAllCellsInFormulaArrays(isRow bool) (map[string]bool, error) {
|
||||
ev := formula.NewEvaluator()
|
||||
ctx := s.FormulaContext()
|
||||
cellsInFormulaArrays := map[string]bool{}
|
||||
for _, r := range s.Rows() {
|
||||
for _, c := range r.Cells() {
|
||||
if c.X().F != nil {
|
||||
formStr := c.X().F.Content
|
||||
if c.X().F.TAttr == sml.ST_CellFormulaTypeArray {
|
||||
res := ev.Eval(ctx, formStr).AsString()
|
||||
if res.Type == formula.ResultTypeError {
|
||||
unioffice.Log("error evaulating formula %s: %s", formStr, res.ErrorMessage)
|
||||
c.X().V = nil
|
||||
}
|
||||
if res.Type == formula.ResultTypeArray {
|
||||
cref, err := reference.ParseCellReference(c.Reference())
|
||||
if err != nil {
|
||||
return map[string]bool{}, err
|
||||
}
|
||||
if (isRow && len(res.ValueArray) == 1) || (!isRow && len(res.ValueArray[0]) == 1) {
|
||||
continue
|
||||
}
|
||||
for ir, row := range res.ValueArray {
|
||||
rowIdx := cref.RowIdx + uint32(ir)
|
||||
for ic := range row {
|
||||
column := reference.IndexToColumn(cref.ColumnIdx + uint32(ic))
|
||||
cellsInFormulaArrays[fmt.Sprintf("%s%d", column, rowIdx)] = true
|
||||
}
|
||||
}
|
||||
} else if res.Type == formula.ResultTypeList {
|
||||
cref, err := reference.ParseCellReference(c.Reference())
|
||||
if err != nil {
|
||||
return map[string]bool{}, err
|
||||
}
|
||||
if isRow || len(res.ValueList) == 1 {
|
||||
continue
|
||||
}
|
||||
rowIdx := cref.RowIdx
|
||||
for ic := range res.ValueList {
|
||||
column := reference.IndexToColumn(cref.ColumnIdx + uint32(ic))
|
||||
cellsInFormulaArrays[fmt.Sprintf("%s%d", column, rowIdx)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cellsInFormulaArrays, nil
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/unidoc/unioffice"
|
||||
"github.com/unidoc/unioffice/spreadsheet"
|
||||
"github.com/unidoc/unioffice/spreadsheet/reference"
|
||||
)
|
||||
|
||||
func TestRowNumIncreases(t *testing.T) {
|
||||
@ -162,6 +163,16 @@ func TestMergedCell(t *testing.T) {
|
||||
t.Errorf("expected merged cell content to be '%s', got '%s'", expContent, mc.Cell().GetString())
|
||||
}
|
||||
|
||||
sheet.RemoveColumn("B")
|
||||
if mc.Cell().GetString() != expContent {
|
||||
t.Errorf("expected merged cell content to be '%s', got '%s'", expContent, mc.Cell().GetString())
|
||||
}
|
||||
|
||||
sheet.RemoveColumn("A")
|
||||
if mc.Cell().GetString() != "" {
|
||||
t.Errorf("expected merged cell content to be '%s', got '%s'", expContent, mc.Cell().GetString())
|
||||
}
|
||||
|
||||
sheet.RemoveMergedCell(mc)
|
||||
if len(sheet.MergedCells()) != 0 {
|
||||
t.Errorf("after removal, sheet should have no merged cells")
|
||||
@ -334,3 +345,27 @@ func TestSortStrings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveColumn(t *testing.T) {
|
||||
wb := spreadsheet.New()
|
||||
sheet := wb.AddSheet()
|
||||
sheet.Cell("A1").SetNumber(5)
|
||||
sheet.Cell("B1").SetNumber(4)
|
||||
sheet.Cell("C1").SetNumber(3)
|
||||
sheet.Cell("D1").SetNumber(2)
|
||||
sheet.Cell("E1").SetNumber(1)
|
||||
|
||||
sheet.RemoveColumn("C")
|
||||
|
||||
expected := []float64{5,4,2,1,0}
|
||||
|
||||
for i := 0; i <= 4; i++ {
|
||||
column := reference.IndexToColumn(uint32(i))
|
||||
ref := fmt.Sprintf("%s1", column)
|
||||
got, _ := sheet.Cell(ref).GetValueAsNumber()
|
||||
exp := expected[i]
|
||||
if got != exp {
|
||||
t.Errorf("expected %f in %s, got %f", exp, ref, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
31
spreadsheet/update/update_query.go
Normal file
31
spreadsheet/update/update_query.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2017 FoxyUtils ehf. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by the terms of the Affero GNU General
|
||||
// Public License version 3.0 as published by the Free Software Foundation and
|
||||
// appearing in the file LICENSE included in the packaging of this file. A
|
||||
// commercial license can be purchased on https://unidoc.io.
|
||||
|
||||
// Package update contains definitions needed for updating references after removing rows/columns.
|
||||
package update
|
||||
|
||||
// UpdateAction is the type for update types constants.
|
||||
type UpdateAction byte
|
||||
const (
|
||||
// UpdateActionRemoveColumn means updating references after removing a column.
|
||||
UpdateActionRemoveColumn UpdateAction = iota
|
||||
)
|
||||
|
||||
// UpdateQuery contains terms of how to update references after removing row/column.
|
||||
type UpdateQuery struct {
|
||||
// UpdateType is one of the update types like UpdateActionRemoveColumn.
|
||||
UpdateType UpdateAction
|
||||
|
||||
// ColumnIdx is the index of the column removed.
|
||||
ColumnIdx uint32
|
||||
|
||||
// SheetToUpdate contains the name of the sheet on which removing happened.
|
||||
SheetToUpdate string
|
||||
|
||||
// UpdateCurrentSheet is true if references without sheet prefix should be updated as well.
|
||||
UpdateCurrentSheet bool
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user