UNPKG

devexpress-richedit

Version:

DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.

237 lines (236 loc) 14 kB
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { TablePosition } from '../../../../model/tables/main-structures/table'; import { TableLayoutType } from '../../../../model/tables/secondary-structures/table-base-structures'; import { TableWidthUnitType } from '../../../../model/tables/secondary-structures/table-units'; import { ColumnIntervalAuto, GridColumnBase } from './column-interval'; import { GridCalculator } from './grid-calculator'; import { TableCellWidthCalculator } from './table-width-calculators/table-cell-width-calculator'; export class GridCalculatorAuto extends GridCalculator { makeInterval(interval) { return new ColumnIntervalAuto(interval); } applyCellsWidth(_intervals) { this.calcCacheCellWidths(); this.applyCellContentWidth(); } autofitTail(totalWidth, estimatedTableWidth) { if (this.currCache.layoutType == TableLayoutType.Fixed) return; const totalMinWidth = GridColumnBase.totalMinWidth(this.columns); let maxPermissibleTableWidth = totalMinWidth; if (estimatedTableWidth <= this.percentBaseWidth) maxPermissibleTableWidth = Math.min(maxPermissibleTableWidth, this.percentBaseWidth); maxPermissibleTableWidth = Math.max(estimatedTableWidth, maxPermissibleTableWidth); if (!this.currCache.isFixedTableWidth) maxPermissibleTableWidth = Math.min(this.percentBaseWidth, maxPermissibleTableWidth); if (totalWidth > maxPermissibleTableWidth) { if (maxPermissibleTableWidth > totalMinWidth) { this.compressTableGrid(maxPermissibleTableWidth); } else { this.compressProportionallyMinWidth(maxPermissibleTableWidth, totalMinWidth); } } } compressProportionallyMinWidth(totalWidth, rest) { for (let i = 0; i < this.columns.length; i++) { const column = this.columns[i]; const newColumnWidth = rest > 0 ? Math.max(column.bounds.minElement * totalWidth / rest, GridCalculator.minColumnWidth) : GridCalculator.minColumnWidth; column.width = newColumnWidth; totalWidth -= newColumnWidth; rest -= column.bounds.minElement; } } calcCacheCellWidths() { const pos = new TablePosition(this.table, -1, -1); while (pos.moveToNextRow()) while (pos.moveToNextCell()) new TableCellWidthCalculator(this.subDocument, this.boxIterator, this.grid, this.cache, pos, this.percentBaseWidth).cellWidth(); } applyCellContentWidth() { const pos = new TablePosition(this.table, -1, -1); while (pos.moveToNextRow()) { let columnIndex = pos.row.gridBefore; while (pos.moveToNextCell()) { if (pos.cell.columnSpan == 1) this.applyCellContentWidthWithoutSpan(this.columns, pos, columnIndex); columnIndex += pos.cell.columnSpan; } } for (let column of this.columns) { if (column.bounds.minElement == 0 && column.bounds.maxElement == 0) { if (column.isPercentBased) column.bounds.minElement = column.bounds.maxElement = 1; else column.bounds.minElement = column.bounds.maxElement = Math.max(1, column.width); } } pos.initIndexes(-1, -1); while (pos.moveToNextRow()) { let columnIndex = pos.row.gridBefore; while (pos.moveToNextCell()) { if (pos.cell.columnSpan > 1) this.applyCellContentWidthWithSpan(this.columns, pos, columnIndex); columnIndex += pos.cell.columnSpan; } } for (let column of this.columns) column.width = column.bounds.maxElement = Math.max(column.bounds.maxElement, GridCalculator.minColumnWidth); } applyCellContentWidthWithoutSpan(columns, pos, columnIndex) { const cellCache = this.currCache.rows[pos.rowIndex].cells[pos.cellIndex]; const info = cellCache.contentWidthsInfo; const cellMinWidth = Math.max(GridCalculator.minColumnWidth, info.minElement); let cellMaxWidth = Math.max(GridCalculator.minColumnWidth, info.maxElement); const column = columns[columnIndex]; column.updateMinBound(cellMinWidth); column.updateMaxBound(cellMaxWidth); column.totalHorizontalMargins = Math.max(column.totalHorizontalMargins, cellCache.horizontalMargins); const preferredWidth = pos.cell.preferredWidth; const cellWidthType = preferredWidth.type; if (cellWidthType == TableWidthUnitType.FiftiethsOfPercent) { if (preferredWidth.value > 0) { column.type = TableWidthUnitType.FiftiethsOfPercent; const percentWidth = Math.min(this.maxPercentWidth, preferredWidth.value); column.percentValue = Math.max(percentWidth, column.percentValue); } else if (this.currCache.isFixedTableWidth) { this.applyPreferredWidth(column, cellMinWidth, 0); } } else if (cellWidthType == TableWidthUnitType.ModelUnits) this.applyPreferredWidth(column, cellMinWidth, preferredWidth.asNumber(this.percentBaseWidth, UnitConverter.twipsToPixels)); } applyPreferredWidth(column, cellMinWidth, preferredWidth) { preferredWidth = Math.max(cellMinWidth, preferredWidth); column.preferredWidth = Math.max(column.preferredWidth, preferredWidth); column.updateMaxBound(column.preferredWidth); } applyCellContentWidthWithSpan(columns, pos, startColumnIndex) { const endColumnIndex = startColumnIndex + pos.cell.columnSpan - 1; const nextEndColumnIndex = endColumnIndex + 1; const cellCache = this.currCache.rows[pos.rowIndex].cells[pos.cellIndex]; const info = cellCache.contentWidthsInfo; const cellMinWidth = Math.max(GridCalculator.minColumnWidth, info.minElement); let cellMaxWidth = Math.max(GridCalculator.minColumnWidth, info.maxElement); const preferredWidth = pos.cell.preferredWidth; const lastColumn = this.columns[endColumnIndex]; if (preferredWidth.type == TableWidthUnitType.FiftiethsOfPercent) { const totalPercent = GridColumnBase.totalPercentWidth(this.columns, startColumnIndex, nextEndColumnIndex); const currrentPercentValue = Math.min(this.maxPercentWidth, preferredWidth.value); if (ListUtils.allOf(this.columns, (col) => col.isPercentBased, startColumnIndex, nextEndColumnIndex)) { const totalBefore = GridColumnBase.totalPercentWidth(this.columns, startColumnIndex, endColumnIndex); lastColumn.percentValue = Math.max(currrentPercentValue - totalBefore, lastColumn.percentValue); } else if (totalPercent > 0) { const percentRest = Math.max(0, currrentPercentValue - totalPercent); let totaNoPercentMaxWidth = 0; ListUtils.forEach(this.columns, (col) => { if (!col.isPercentBased) totaNoPercentMaxWidth += col.bounds.maxElement; }, startColumnIndex, nextEndColumnIndex); ListUtils.forEach(this.columns, (col) => { if (!col.isPercentBased) { col.percentValue = Math.max(1, percentRest * col.bounds.maxElement / totaNoPercentMaxWidth); col.type = TableWidthUnitType.FiftiethsOfPercent; } }, startColumnIndex, nextEndColumnIndex); } } else if (preferredWidth.type == TableWidthUnitType.ModelUnits) { let preferredWidthValue = preferredWidth.asNumber(this.percentBaseWidth, UnitConverter.twipsToPixelsF); preferredWidthValue = Math.max(cellMinWidth, preferredWidthValue); cellMaxWidth = preferredWidthValue; if (ListUtils.allOf(this.columns, (col) => { return !col.isPercentBased && col.preferredWidth > 0; }, startColumnIndex, nextEndColumnIndex)) { const totalMaxWidthBefore = GridColumnBase.totalMaxWidth(this.columns, startColumnIndex, endColumnIndex); lastColumn.preferredWidth = Math.max(lastColumn.preferredWidth, preferredWidthValue - totalMaxWidthBefore); lastColumn.updateMaxBound(lastColumn.preferredWidth); const gridMinWidth = GridColumnBase.totalMinWidth(this.columns, startColumnIndex, nextEndColumnIndex); if (cellMinWidth > gridMinWidth) this.enlargeColumnsMinWidthByPreferredWidth(startColumnIndex, endColumnIndex, cellMinWidth, preferredWidthValue); return; } } const gridMinWidth = ColumnIntervalAuto.totalMinWidth(columns, startColumnIndex, nextEndColumnIndex); if (cellMinWidth > gridMinWidth) this.enlargeColumnsMinWidth(columns, startColumnIndex, nextEndColumnIndex, cellMinWidth); const gridMaxWidth = ColumnIntervalAuto.totalMaxWidth(columns, startColumnIndex, nextEndColumnIndex); if (cellMaxWidth > gridMaxWidth) this.enlargeColumnsMaxWidth(columns, startColumnIndex, nextEndColumnIndex, gridMaxWidth, cellMaxWidth); const gridTotalMargins = ColumnIntervalAuto.totalHorizontalMargins(columns, startColumnIndex, nextEndColumnIndex); if (cellCache.horizontalMargins > gridTotalMargins) this.enlargeColumnsHorizontalMargins(columns, startColumnIndex, nextEndColumnIndex, gridTotalMargins, cellCache.horizontalMargins); } enlargeColumnsMinWidthByPreferredWidth(startColumnIndex, endColumnIndex, cellMinWidth, totalPreferredWidth) { let restMinWidth = cellMinWidth; let restTotalPreferredWidth = totalPreferredWidth; for (let i = endColumnIndex; i >= startColumnIndex; i--) { const column = this.columns[i]; if (column.preferredWidth > 0 && restTotalPreferredWidth > 0) { column.bounds.minElement = column.preferredWidth * restMinWidth / restTotalPreferredWidth; column.updateMaxBound(column.bounds.minElement); restMinWidth -= column.bounds.minElement; restTotalPreferredWidth -= column.preferredWidth; } } } enlargeColumnsHorizontalMargins(columns, startColumnIndex, endColumnIndex, oldWidth, newWidth) { const equalSpace = oldWidth == 0; let totalDelta = newWidth - oldWidth; let totalCount = endColumnIndex - startColumnIndex + 1; for (let i = endColumnIndex - 1, column; (column = columns[i]) && totalDelta > 0; i--) { const delta = equalSpace ? totalDelta / totalCount : totalDelta * column.totalHorizontalMargins / oldWidth; totalDelta -= delta; oldWidth -= column.totalHorizontalMargins; column.totalHorizontalMargins += delta; totalCount--; } } enlargeColumnsMinWidth(columns, startColumnIndex, endColumnIndex, newWidth) { const hasColumnsWithoutPreferredWidth = ColumnIntervalAuto.hasColumnsWithoutPreferredWidth(columns, startColumnIndex, endColumnIndex); let zeroMinWidthCount = 0; let existingMinWidth = 0; ListUtils.forEach(columns, (column) => { if (column.bounds.minElement == 0 && column.bounds.maxElement == 0) zeroMinWidthCount++; else existingMinWidth += column.bounds.minElement; }, startColumnIndex, endColumnIndex); let rest = ColumnIntervalAuto.totalMaxWidth(columns, startColumnIndex, endColumnIndex) + ColumnIntervalAuto.totalMinWidth(columns, startColumnIndex, endColumnIndex); const equalSpace = rest == 0; if (equalSpace || zeroMinWidthCount > 0) { rest = endColumnIndex - startColumnIndex; newWidth -= existingMinWidth; } ListUtils.reverseForEach(columns, (column) => { if (!hasColumnsWithoutPreferredWidth || column.preferredWidth == 0) { if (zeroMinWidthCount > 0 && (column.bounds.minElement > 0 || column.bounds.maxElement > 0)) return; const factor = (equalSpace || zeroMinWidthCount > 0) ? 1 : (column.bounds.minElement + column.bounds.maxElement); const newMinWidth = factor * newWidth / rest; rest -= factor; newWidth -= newMinWidth; column.updateMinBound(newMinWidth); column.updateMaxBound(column.bounds.minElement); } }, endColumnIndex - 1, startColumnIndex); } enlargeColumnsMaxWidth(columns, startColumnIndex, endColumnIndex, oldWidth, newWidth) { const allColumnsHavePreferredWidth = ListUtils.allOf(columns, (col) => col.preferredWidth != 0, startColumnIndex, endColumnIndex); let rest = oldWidth; ListUtils.reverseForEach(columns, (column) => { if (allColumnsHavePreferredWidth || column.preferredWidth == 0) { const newMaxWidth = rest != 0 ? column.bounds.maxElement * newWidth / rest : 0; rest -= column.bounds.maxElement; newWidth -= newMaxWidth; if (rest < 0) rest = 0; if (newWidth < 0) newWidth = 0; column.bounds.maxElement = Math.max(GridCalculator.minColumnWidth, newMaxWidth); } }, endColumnIndex - 1, startColumnIndex); } }