use
5.006 ;
our
@ISA
=
qw(Exporter)
;
our
%EXPORT_TAGS
=
(
'all'
=> [
qw()
]
) ;
our
@EXPORT_OK
= ( @{
$EXPORT_TAGS
{
'all'
} } ) ;
our
@EXPORT
;
push
@EXPORT
,
qw()
;
our
$VERSION
=
'0.03'
;
sub
InsertRows
{
my
(
$self
,
$start_row
,
$number_of_rows_to_insert
) =
@_
;
confess
"Invalid row '$start_row'\n"
unless
$start_row
=~ /^\s*\d+\s*$/ ;
my
(
%moved_cell_list
,
%not_moved_cell_list
) ;
for
my
$cell_address
(
$self
->GetCellList())
{
my
(
$column
,
$row
) =
$cell_address
=~ /([A-Z]+)(\d+)/ ;
if
(
$row
>=
$start_row
)
{
push
@{
$moved_cell_list
{
$row
}},
$cell_address
;
}
else
{
push
@{
$not_moved_cell_list
{
$row
}},
$cell_address
;
}
}
for
my
$row
(
reverse
sort
keys
%moved_cell_list
)
{
for
my
$cell_address
(@{
$moved_cell_list
{
$row
}})
{
my
$new_address
=
$self
->OffsetAddress(
$cell_address
, 0,
$number_of_rows_to_insert
) ;
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
$self
->OffsetFormula(
$cell_address
, 0, 0,
$start_row
,
$number_of_rows_to_insert
,
"A$start_row:AAAA9999"
) ;
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->OffsetDependents(
$cell_address
, 0, 0,
$start_row
,
$number_of_rows_to_insert
,
"A$start_row:AAAA9999"
) ;
}
$self
->{CELLS}{
$new_address
} =
$self
->{CELLS}{
$cell_address
} ;
delete
$self
->{CELLS}{
$cell_address
} ;
}
}
for
my
$row
(
reverse
sort
keys
%not_moved_cell_list
)
{
for
my
$cell_address
(@{
$not_moved_cell_list
{
$row
}})
{
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
$self
->OffsetFormula(
$cell_address
, 0, 0,
$start_row
,
$number_of_rows_to_insert
,
"A$start_row:AAAA9999"
) ;
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->OffsetDependents(
$cell_address
, 0, 0,
$start_row
,
$number_of_rows_to_insert
,
"A$start_row:AAAA9999"
) ;
}
}
}
for
my
$row_header
(
reverse
SortCells
grep
{/^@/}
$self
->GetCellHeaderList())
{
my
(
$row_index
) =
$row_header
=~ /^@(.+)/ ;
if
(
$row_index
>=
$start_row
)
{
my
$new_row
=
$row_index
+
$number_of_rows_to_insert
;
$self
->{CELLS}{
"\@$new_row"
} =
$self
->{CELLS}{
$row_header
} ;
delete
$self
->{CELLS}{
$row_header
} ;
}
}
}
sub
InsertColumns
{
my
(
$self
,
$start_column
,
$number_of_columns_to_insert
) =
@_
;
confess
"Invalid w '$start_column'\n"
unless
$start_column
=~ /^\s*[A-Z]{1,4}\s*$/ ;
my
(
%moved_cell_list
,
%not_moved_cell_list
) ;
for
my
$cell_address
(
$self
->GetCellList())
{
my
(
$column
,
$row
) =
$cell_address
=~ /([A-Z]+)(\d+)/ ;
my
$column_index
= FromAA(
$column
) ;
my
$start_column_index
= FromAA(
$start_column
) ;
if
(
$column_index
>=
$start_column_index
)
{
push
@{
$moved_cell_list
{
$column_index
}},
$cell_address
;
}
else
{
push
@{
$not_moved_cell_list
{
$column_index
}},
$cell_address
;
}
}
for
my
$column_index
(
reverse
sort
keys
%moved_cell_list
)
{
for
my
$cell_address
(@{
$moved_cell_list
{
$column_index
}})
{
my
$new_address
=
$self
->OffsetAddress(
$cell_address
,
$number_of_columns_to_insert
, 0) ;
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
$self
->OffsetFormula(
$cell_address
,
$start_column
,
$number_of_columns_to_insert
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->OffsetDependents(
$cell_address
,
$start_column
,
$number_of_columns_to_insert
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
$self
->{CELLS}{
$new_address
} =
$self
->{CELLS}{
$cell_address
} ;
delete
$self
->{CELLS}{
$cell_address
} ;
}
}
for
my
$column_index
(
reverse
sort
keys
%not_moved_cell_list
)
{
for
my
$cell_address
(@{
$not_moved_cell_list
{
$column_index
}})
{
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
$self
->OffsetFormula(
$cell_address
,
$start_column
,
$number_of_columns_to_insert
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->OffsetDependents(
$cell_address
,
$start_column
,
$number_of_columns_to_insert
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
}
}
my
$start_column_index
= FromAA(
$start_column
) ;
for
my
$column_header
(
reverse
SortCells
grep
{/^[A-Z]+0$/}
$self
->GetCellHeaderList())
{
my
(
$column_index
) =
$column_header
=~ /^([A-Z]+)0$/ ;
$column_index
= FromAA(
$column_index
) ;
if
(
$column_index
>=
$start_column_index
)
{
my
$new_column
=
$column_index
+
$number_of_columns_to_insert
;
$new_column
= ToAA(
$new_column
) ;
$self
->{CELLS}{
"${new_column}0"
} =
$self
->{CELLS}{
$column_header
} ;
delete
$self
->{CELLS}{
$column_header
} ;
}
}
}
sub
OffsetFormula
{
my
(
$self
,
$cell_address
,
$start_column
,
$columns_to_insert
,
$start_row
,
$rows_to_insert
,
$range
) =
@_
;
return
unless
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA} ;
my
$formula
=
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA} ;
$formula
=~ s/(\[?[A-Z]+\]?\[?[0-9]+\]?(:\[?[A-Z]+\]?\[?[0-9]+\]?)?)/
$self
->OffsetAddress($1,
$columns_to_insert
,
$rows_to_insert
,
$range
)/eg ;
$self
->Set(
$cell_address
, PF(
$formula
)) ;
}
sub
OffsetDependents
{
my
(
$self
,
$cell_address
,
$start_column
,
$columns_to_insert
,
$start_row
,
$rows_to_insert
,
$range
) =
@_
;
return
unless
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT} ;
my
$dependents
=
$self
->{CELLS}{
$cell_address
}{DEPENDENT} ;
my
@new_dependents
;
for
my
$dependent_name
(
keys
%{
$dependents
})
{
my
$dependent
=
$dependents
->{
$dependent_name
} ;
my
(
$spreadsheet
,
$cell_name
) = @{
$dependent
->{DEPENDENT_DATA}} ;
my
$new_cell_name
=
$self
->OffsetAddress(
$cell_name
,
$columns_to_insert
,
$rows_to_insert
,
$range
) ;
$dependent
->{DEPENDENT_DATA}[1] =
$new_cell_name
;
push
@new_dependents
,
$dependent
;
delete
$dependents
->{
$dependent_name
} ;
}
for
my
$dependent
(
@new_dependents
)
{
my
$dependent_name
=
$dependent
->{DEPENDENT_DATA}[2] .
'!'
.
$dependent
->{DEPENDENT_DATA}[1] ;
$dependents
->{
$dependent_name
} =
$dependent
;
}
}
sub
DeleteDependents
{
my
(
$self
,
$cell_address
,
$range
) =
@_
;
return
unless
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT} ;
my
$dependents
=
$self
->{CELLS}{
$cell_address
}{DEPENDENT} ;
my
@new_dependents
;
for
my
$dependent_name
(
keys
%{
$dependents
})
{
my
$dependent
=
$dependents
->{
$dependent_name
} ;
my
(
$spreadsheet
,
$cell_name
) = @{
$dependent
->{DEPENDENT_DATA}} ;
if
(
$self
->is_within_range(
$cell_name
,
$range
))
{
delete
$dependents
->{
$dependent_name
} ;
}
}
}
sub
DeleteColumns
{
my
(
$self
,
$start_column
,
$number_of_columns_to_delete
) =
@_
;
confess
"Invalid '$start_column'\n"
unless
$start_column
=~ /^\s*[A-Z]{1,4}\s*$/ ;
my
$start_column_index
= FromAA(
$start_column
) ;
my
$end_column
= ToAA(
$start_column_index
+
$number_of_columns_to_delete
- 1) ;
my
(
%removed_cell_list
,
%moved_cell_list
,
%not_moved_cell_list
) ;
for
my
$cell_address
(
$self
->GetCellList())
{
my
(
$column
,
$row
) =
$cell_address
=~ /([A-Z]+)(\d+)/ ;
my
$column_index
= FromAA(
$column
) ;
my
$start_column_index
= FromAA(
$start_column
) ;
if
(
$column_index
>=
$start_column_index
)
{
if
(
$column_index
<
$start_column_index
+
$number_of_columns_to_delete
)
{
push
@{
$removed_cell_list
{
$column_index
}},
$cell_address
;
}
else
{
push
@{
$moved_cell_list
{
$column_index
}},
$cell_address
;
}
}
else
{
push
@{
$not_moved_cell_list
{
$column_index
}},
$cell_address
;
}
}
for
my
$column_index
(
keys
%removed_cell_list
)
{
for
my
$cell_address
(@{
$removed_cell_list
{
$column_index
}})
{
$self
->DELETE(
$cell_address
) ;
}
}
for
my
$column_index
(
sort
keys
%moved_cell_list
)
{
for
my
$cell_address
(@{
$moved_cell_list
{
$column_index
}})
{
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
if
(
$self
->FormulaReferenceRange(
$cell_address
,
"${start_column}1:${end_column}9999"
))
{
$self
->Set(
$cell_address
, PF(
"'#REF [dc]'"
)) ;
}
else
{
$self
->OffsetFormula(
$cell_address
,
$start_column
, -
$number_of_columns_to_delete
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->DeleteDependents(
$cell_address
,
"${start_column}1:${end_column}9999"
) ;
$self
->OffsetDependents(
$cell_address
,
$start_column
, -
$number_of_columns_to_delete
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
my
$new_address
=
$self
->OffsetAddress(
$cell_address
, -
$number_of_columns_to_delete
, 0) ;
$self
->{CELLS}{
$new_address
} =
$self
->{CELLS}{
$cell_address
} ;
delete
$self
->{CELLS}{
$cell_address
} ;
}
}
for
my
$column_index
(
reverse
sort
keys
%not_moved_cell_list
)
{
for
my
$cell_address
(@{
$not_moved_cell_list
{
$column_index
}})
{
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
if
(
$self
->FormulaReferenceRange(
$cell_address
,
"${start_column}1:${end_column}9999"
))
{
$self
->Set(
$cell_address
, PF(
"'#REF [dc]'"
)) ;
}
else
{
$self
->OffsetFormula(
$cell_address
,
$start_column
, -
$number_of_columns_to_delete
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->DeleteDependents(
$cell_address
,
"${start_column}1:${end_column}9999"
) ;
$self
->OffsetDependents(
$cell_address
,
$start_column
, -
$number_of_columns_to_delete
, 0, 0,
"${start_column}1:AAAA9999"
) ;
}
}
}
for
my
$column_header
(SortCells
grep
{!/^@/}
$self
->GetCellHeaderList())
{
my
(
$column_index
) =
$column_header
=~ /^([A-Z@]+)0$/ ;
$column_index
= FromAA(
$column_index
) ;
if
(
$column_index
>=
$start_column_index
)
{
if
(
$column_index
<
$start_column_index
+
$number_of_columns_to_delete
)
{
$self
->DELETE(
$column_header
) ;
}
else
{
my
$new_column
=
$column_index
-
$number_of_columns_to_delete
;
$new_column
= ToAA(
$new_column
) ;
$self
->{CELLS}{
"${new_column}0"
} =
$self
->{CELLS}{
$column_header
} ;
delete
$self
->{CELLS}{
$column_header
} ;
}
}
}
}
sub
DeleteRows
{
my
(
$self
,
$start_row
,
$number_of_rows_to_delete
) =
@_
;
confess
"Invalid '$start_row'\n"
unless
$start_row
=~ /^\s*\d+\s*$/ ;
my
$end_row
=
$start_row
+
$number_of_rows_to_delete
- 1 ;
my
(
%removed_cell_list
,
%moved_cell_list
,
%not_moved_cell_list
) ;
for
my
$cell_address
(
$self
->GetCellList())
{
my
(
$column
,
$row
) =
$cell_address
=~ /([A-Z]+)(\d+)/ ;
if
(
$row
>=
$start_row
)
{
if
(
$row
<
$start_row
+
$number_of_rows_to_delete
)
{
push
@{
$removed_cell_list
{
$row
}},
$cell_address
;
}
else
{
push
@{
$moved_cell_list
{
$row
}},
$cell_address
;
}
}
else
{
push
@{
$not_moved_cell_list
{
$row
}},
$cell_address
;
}
}
for
my
$row
(
keys
%removed_cell_list
)
{
for
my
$cell_address
(@{
$removed_cell_list
{
$row
}})
{
$self
->DELETE(
$cell_address
) ;
}
}
for
my
$row
(
sort
keys
%moved_cell_list
)
{
for
my
$cell_address
(@{
$moved_cell_list
{
$row
}})
{
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
if
(
$self
->FormulaReferenceRange(
$cell_address
,
"A${start_row}:AAAA${end_row}"
))
{
$self
->Set(
$cell_address
, PF(
"'#REF [dr]'"
)) ;
}
else
{
$self
->OffsetFormula(
$cell_address
, 0, 0,
$start_row
, -
$number_of_rows_to_delete
,
"A${start_row}:AAAA9999"
) ;
}
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->DeleteDependents(
$cell_address
,
"A${start_row}:AAAA${end_row}"
) ;
$self
->OffsetDependents(
$cell_address
, 0, 0,
$start_row
, -
$number_of_rows_to_delete
,
"A${start_row}:AAAA9999"
) ;
}
my
$new_address
=
$self
->OffsetAddress(
$cell_address
, 0, -
$number_of_rows_to_delete
) ;
$self
->{CELLS}{
$new_address
} =
$self
->{CELLS}{
$cell_address
} ;
delete
$self
->{CELLS}{
$cell_address
} ;
}
}
for
my
$row
(
reverse
sort
keys
%not_moved_cell_list
)
{
for
my
$cell_address
(@{
$not_moved_cell_list
{
$row
}})
{
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
if
(
$self
->FormulaReferenceRange(
$cell_address
,
"A${start_row}:AAAA${end_row}"
))
{
$self
->Set(
$cell_address
, PF(
"'#REF [dr]'"
)) ;
}
else
{
$self
->OffsetFormula(
$cell_address
, 0, 0,
$start_row
, -
$number_of_rows_to_delete
,
"A${start_row}:AAAA9999"
) ;
}
}
if
(
exists
$self
->{CELLS}{
$cell_address
}{DEPENDENT})
{
$self
->DeleteDependents(
$cell_address
,
"A${start_row}:AAAA${end_row}"
) ;
$self
->OffsetDependents(
$cell_address
, 0, 0,
$start_row
, -
$number_of_rows_to_delete
,
"A${start_row}:AAAA9999"
) ;
}
}
}
for
my
$row_header
(
sort
grep
{/^@/}
$self
->GetCellHeaderList())
{
my
(
$row
) =
$row_header
=~ /(\d+)$/ ;
if
(
$row
>=
$start_row
)
{
if
(
$row
<
$start_row
+
$number_of_rows_to_delete
)
{
$self
->DELETE(
$row_header
) ;
}
else
{
my
$new_row
=
$row
-
$number_of_rows_to_delete
;
$self
->{CELLS}{
"\@${new_row}"
} =
$self
->{CELLS}{
$row_header
} ;
delete
$self
->{CELLS}{
$row_header
} ;
}
}
}
}
sub
FormulaReferenceRange
{
my
(
$self
,
$cell_address
,
$range
) =
@_
;
if
(
exists
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA})
{
my
(
$rcs
,
$rrs
,
$rce
,
$rre
) =
$range
=~/([A-Z]+)([0-9]+):([A-Z]+)([0-9]+)/ ;
unless
(
defined
$rcs
&&
defined
$rrs
&&
defined
$rce
&&
defined
$rre
)
{
confess
"Invalid range '$range'\n"
;
}
(
$rcs
,
$rce
) = (FromAA(
$rcs
), FromAA(
$rce
)) ;
if
(
$rcs
>
$rce
)
{
if
(
$rrs
>
$rre
)
{
(
$rcs
,
$rrs
,
$rce
,
$rre
) = (
$rce
,
$rre
,
$rcs
,
$rrs
) ;
}
else
{
(
$rcs
,
$rce
) = (
$rce
,
$rcs
) ;
}
}
else
{
if
(
$rrs
>
$rre
)
{
(
$rrs
,
$rre
) = (
$rre
,
$rrs
) ;
}
}
my
$formula
=
$self
->{CELLS}{
$cell_address
}{GENERATED_FORMULA} ;
my
(
$fcs
,
$frs
,
$fce
,
$fre
) ;
while
(
$formula
=~ /((([A-Z]+)([0-9]+))(:([A-Z]+)([0-9]+))?)/g)
{
if
(
defined
$5)
{
(
$fcs
,
$frs
,
$fce
,
$fre
) = ($3, $4, $6, $7) ;
(
$fcs
,
$fce
) = (FromAA(
$fcs
), FromAA(
$fce
)) ;
if
(
$fcs
>
$fce
)
{
if
(
$frs
>
$fre
)
{
(
$fcs
,
$frs
,
$fce
,
$fre
) = (
$fce
,
$fre
,
$fcs
,
$frs
) ;
}
else
{
(
$fcs
,
$fce
) = (
$fce
,
$fcs
) ;
}
}
else
{
if
(
$frs
>
$fre
)
{
(
$frs
,
$fre
) = (
$fre
,
$frs
) ;
}
}
}
else
{
(
$fcs
,
$frs
) = ($3, $4) ;
(
$fcs
) = FromAA(
$fcs
) ;
(
$fce
,
$fre
) = (
$fcs
,
$frs
) ;
}
if
(
$fcs
>
$rce
||
$fce
<
$rcs
||
$frs
>
$rre
||
$fre
<
$rrs
)
{
return
0 ;
}
else
{
return
1 ;
}
}
}
else
{
return
0 ;
}
}
1 ;