| @@ -176,15 +176,24 @@ | ||
| 176 | 176 | */ |
| 177 | 177 | public function getFormattedValue(): string |
| 178 | 178 | { |
| 179 | 179 | $currentCalendar = SharedDate::getExcelCalendar(); |
| 180 | - SharedDate::setExcelCalendar(($nullsafeVariable2 = $this->getWorksheet()->getParent()) ? $nullsafeVariable2->getExcelCalendar() : null); | |
| 181 | - $formattedValue = (string) NumberFormat::toFormattedString( | |
| 182 | - $this->getCalculatedValueString(), | |
| 183 | - (string) $this->getStyle()->getNumberFormat()->getFormatCode(true) | |
| 180 | + SharedDate::setExcelCalendar( | |
| 181 | + ($nullsafeVariable2 = $this->getWorksheet() | |
| 182 | + ->getParent()) ? $nullsafeVariable2->getExcelCalendar() : null | |
| 184 | 183 | ); |
| 185 | - SharedDate::setExcelCalendar($currentCalendar); | |
| 186 | 184 | |
| 185 | + try { | |
| 186 | + $formattedValue = NumberFormat::toFormattedString( | |
| 187 | + $this->getCalculatedValueString(), | |
| 188 | + (string) $this->getStyle() | |
| 189 | + ->getNumberFormat() | |
| 190 | + ->getFormatCode(true) | |
| 191 | + ); | |
| 192 | + } finally { | |
| 193 | + SharedDate::setExcelCalendar($currentCalendar); | |
| 194 | + } | |
| 195 | + | |
| 187 | 196 | return $formattedValue; |
| 188 | 197 | } |
| 189 | 198 | /** |
| 190 | 199 | * @param mixed $oldValue |
| @@ -343,19 +352,19 @@ | ||
| 343 | 352 | self::updateIfCellIsTableHeader(($nullsafeVariable6 = $this->getParent()) ? $nullsafeVariable6->getParent() : null, $this, $oldValue, $value); |
| 344 | 353 | $worksheet = $this->getWorksheet(); |
| 345 | 354 | $spreadsheet = $worksheet->getParent(); |
| 346 | 355 | if (isset($spreadsheet) && $spreadsheet->getIndex($worksheet, true) >= 0) { |
| 347 | - $originalSelected = $worksheet->getSelectedCells(); | |
| 348 | - $activeSheetIndex = $spreadsheet->getActiveSheetIndex(); | |
| 349 | - $style = $this->getStyle(); | |
| 350 | - $oldQuotePrefix = $style->getQuotePrefix(); | |
| 356 | + // Avoid Worksheet::getStyle() (selection + validation) unless quotePrefix must change. | |
| 357 | + $oldQuotePrefix = $spreadsheet->getCellXfByIndex($this->getXfIndex())->getQuotePrefix(); | |
| 351 | 358 | if ($oldQuotePrefix !== $quotePrefix) { |
| 352 | - $style->setQuotePrefix($quotePrefix); | |
| 359 | + $originalSelected = $worksheet->getSelectedCells(); | |
| 360 | + $activeSheetIndex = $spreadsheet->getActiveSheetIndex(); | |
| 361 | + $this->getStyle()->setQuotePrefix($quotePrefix); | |
| 362 | + $worksheet->setSelectedCells($originalSelected); | |
| 363 | + if ($activeSheetIndex >= 0) { | |
| 364 | + $spreadsheet->setActiveSheetIndex($activeSheetIndex); | |
| 365 | + } | |
| 353 | 366 | } |
| 354 | - $worksheet->setSelectedCells($originalSelected); | |
| 355 | - if ($activeSheetIndex >= 0) { | |
| 356 | - $spreadsheet->setActiveSheetIndex($activeSheetIndex); | |
| 357 | - } | |
| 358 | 367 | } |
| 359 | 368 | |
| 360 | 369 | return (($nullsafeVariable7 = $this->getParent()) ? $nullsafeVariable7->get($cellCoordinate) : null) ?? $this; |
| 361 | 370 | } |
| @@ -966,8 +975,20 @@ | ||
| 966 | 975 | |
| 967 | 976 | return $this->updateInCollection(); |
| 968 | 977 | } |
| 969 | 978 | /** |
| 979 | + * Set the XF index without triggering updateInCollection(). | |
| 980 | + * | |
| 981 | + * This is intended for use by readers that will immediately follow with | |
| 982 | + * setValueExplicit(), avoiding a redundant cache write. | |
| 983 | + * | |
| 984 | + * @internal | |
| 985 | + */ | |
| 986 | + public function setXfIndexNoUpdate(int $indexValue): void | |
| 987 | + { | |
| 988 | + $this->xfIndex = $indexValue; | |
| 989 | + } | |
| 990 | + /** | |
| 970 | 991 | * Set the formula attributes. |
| 971 | 992 | * |
| 972 | 993 | * @param null|array<string, string> $attributes |
| 973 | 994 | */ |
| @@ -1020,6 +1041,91 @@ | ||
| 1020 | 1041 | } |
| 1021 | 1042 | $hidden = $this->getStyle()->getProtection()->getHidden(); |
| 1022 | 1043 | |
| 1023 | 1044 | return $hidden !== Protection::PROTECTION_UNPROTECTED; |
| 1045 | + } | |
| 1046 | + /** | |
| 1047 | + * Return cell $right positions to the right of this one. | |
| 1048 | + */ | |
| 1049 | + public function cursorRight(int $right = 1): self | |
| 1050 | + { | |
| 1051 | + $row = $this->getRow(); | |
| 1052 | + $col = $this->getColumn(); | |
| 1053 | + $colIndex = Coordinate::columnIndexFromString($col); | |
| 1054 | + $newCol = max( | |
| 1055 | + 1, | |
| 1056 | + min($colIndex + $right, AddressRange::MAX_COLUMN_INT) | |
| 1057 | + ); | |
| 1058 | + $newColStr = Coordinate::stringFromColumnIndex($newCol); | |
| 1059 | + | |
| 1060 | + return $this->getWorksheet()->getCell("$newColStr$row"); | |
| 1061 | + } | |
| 1062 | + /** | |
| 1063 | + * Return cell $left positions to the left of this one. | |
| 1064 | + */ | |
| 1065 | + public function cursorLeft(int $left = 1): self | |
| 1066 | + { | |
| 1067 | + return $this->cursorRight(-$left); | |
| 1068 | + } | |
| 1069 | + /** | |
| 1070 | + * Return cell $down positions below this one. | |
| 1071 | + */ | |
| 1072 | + public function cursorDown(int $down = 1): self | |
| 1073 | + { | |
| 1074 | + $row = $this->getRow(); | |
| 1075 | + $col = $this->getColumn(); | |
| 1076 | + $newRow = max( | |
| 1077 | + 1, | |
| 1078 | + min($row + $down, AddressRange::MAX_ROW) | |
| 1079 | + ); | |
| 1080 | + | |
| 1081 | + return $this->getWorksheet()->getCell("$col$newRow"); | |
| 1082 | + } | |
| 1083 | + /** | |
| 1084 | + * Return cell $up positions above this one. | |
| 1085 | + */ | |
| 1086 | + public function cursorUp(int $up = 1): self | |
| 1087 | + { | |
| 1088 | + return $this->cursorDown(-$up); | |
| 1089 | + } | |
| 1090 | + /** | |
| 1091 | + * Return cell at row $row in current column. | |
| 1092 | + */ | |
| 1093 | + public function cursorRow(int $row = 1): self | |
| 1094 | + { | |
| 1095 | + $col = $this->getColumn(); | |
| 1096 | + $newRow = max( | |
| 1097 | + 1, | |
| 1098 | + min($row, AddressRange::MAX_ROW) | |
| 1099 | + ); | |
| 1100 | + | |
| 1101 | + return $this->getWorksheet()->getCell("$col$newRow"); | |
| 1102 | + } | |
| 1103 | + /** | |
| 1104 | + * Return cell at column $column in current row. | |
| 1105 | + */ | |
| 1106 | + public function cursorColumn(string $column = 'A'): self | |
| 1107 | + { | |
| 1108 | + $row = $this->getRow(); | |
| 1109 | + $colIndex = Coordinate::columnIndexFromString($column); | |
| 1110 | + $newCol = max( | |
| 1111 | + 1, | |
| 1112 | + min($colIndex, AddressRange::MAX_COLUMN_INT) | |
| 1113 | + ); | |
| 1114 | + $newColStr = Coordinate::stringFromColumnIndex($newCol); | |
| 1115 | + | |
| 1116 | + return $this->getWorksheet()->getCell("$newColStr$row"); | |
| 1117 | + } | |
| 1118 | + /** | |
| 1119 | + * Return cell adjusted for Xls limits if applicable. | |
| 1120 | + */ | |
| 1121 | + public function cursorXlsLimits(): self | |
| 1122 | + { | |
| 1123 | + $row = min($this->getRow(), AddressRange::MAX_ROW_XLS); | |
| 1124 | + $column = $this->getColumn(); | |
| 1125 | + $colIndex = Coordinate::columnIndexFromString($column); | |
| 1126 | + $newCol = min($colIndex, AddressRange::MAX_COLUMN_INT_XLS); | |
| 1127 | + $newColStr = Coordinate::stringFromColumnIndex($newCol); | |
| 1128 | + | |
| 1129 | + return $this->getWorksheet()->getCell("$newColStr$row"); | |
| 1024 | 1130 | } |
| 1025 | 1131 | } |