2018-02-13 09:23:53 -08:00
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
// Licensed under the MIT License.
|
2016-03-30 14:20:52 -07:00
|
|
|
|
2018-04-18 19:15:36 +05:00
|
|
|
using System;
|
|
|
|
|
|
2016-03-30 14:20:52 -07:00
|
|
|
namespace Microsoft.PowerShell.Commands.Internal.Format
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// class providing an algorithm for automatic resizing
|
|
|
|
|
/// of table columns
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal sealed class ColumnWidthManager
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// class providing an algorithm for automatic resizing
|
|
|
|
|
/// </summary>
|
2018-12-28 16:39:06 +05:00
|
|
|
/// <param name="tableWidth">overall width of the table in characters.</param>
|
|
|
|
|
/// <param name="minimumColumnWidth">minimum usable column width.</param>
|
|
|
|
|
/// <param name="separatorWidth">number of separator characters.</param>
|
2016-07-29 13:02:49 -07:00
|
|
|
internal ColumnWidthManager(int tableWidth, int minimumColumnWidth, int separatorWidth)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
_tableWidth = tableWidth;
|
|
|
|
|
_minimumColumnWidth = minimumColumnWidth;
|
|
|
|
|
_separatorWidth = separatorWidth;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2017-01-16 13:31:14 -08:00
|
|
|
/// calculate the widths by applying some heuristics to get them to fit on the
|
2016-03-30 14:20:52 -07:00
|
|
|
/// allotted table width. It first assigns widths to the columns that do not have a specified
|
|
|
|
|
/// width, then it checks if the total width exceeds the screen widths. If so, it proceeds
|
|
|
|
|
/// with column elimination, starting from the right most column
|
|
|
|
|
/// </summary>
|
2018-12-28 16:39:06 +05:00
|
|
|
/// <param name="columnWidths">array of column widths to appropriately size.</param>
|
2018-04-18 19:15:36 +05:00
|
|
|
internal void CalculateColumnWidths(Span<int> columnWidths)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
if (AssignColumnWidths(columnWidths))
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
// we do not have any trimming to do, we are done
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// total width exceeds screen width, go on with trimming
|
2016-07-29 13:02:49 -07:00
|
|
|
TrimToFit(columnWidths);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// do not remove columns, just assign widths to columns that have a zero width
|
|
|
|
|
/// (meaning unassigned)
|
|
|
|
|
/// </summary>
|
2018-12-28 16:39:06 +05:00
|
|
|
/// <param name="columnWidths">columns to process.</param>
|
2018-12-25 21:43:03 +05:00
|
|
|
/// <returns>True if there was a fit, false if there is need for trimming.</returns>
|
2018-04-18 19:15:36 +05:00
|
|
|
private bool AssignColumnWidths(Span<int> columnWidths)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
// run a quick check to see if all the columns have a specified width,
|
|
|
|
|
// if so, we are done
|
|
|
|
|
bool allSpecified = true;
|
|
|
|
|
int maxInitialWidthSum = 0;
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < columnWidths.Length; k++)
|
|
|
|
|
{
|
|
|
|
|
if (columnWidths[k] <= 0)
|
|
|
|
|
{
|
|
|
|
|
allSpecified = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maxInitialWidthSum += columnWidths[k];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (allSpecified)
|
|
|
|
|
{
|
|
|
|
|
// compute the total table width (columns and separators)
|
2016-07-29 13:02:49 -07:00
|
|
|
maxInitialWidthSum += _separatorWidth * (columnWidths.Length - 1);
|
|
|
|
|
if (maxInitialWidthSum <= _tableWidth)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
// we fit with all the columns specified
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// we do not fit, we will have to trim
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we have columns with no width assigned
|
|
|
|
|
// remember the columns we are trying to size
|
|
|
|
|
// assign them the minimum column size
|
|
|
|
|
bool[] fixedColumn = new bool[columnWidths.Length];
|
|
|
|
|
for (int k = 0; k < columnWidths.Length; k++)
|
|
|
|
|
{
|
|
|
|
|
fixedColumn[k] = columnWidths[k] > 0;
|
|
|
|
|
if (columnWidths[k] == 0)
|
2016-07-29 13:02:49 -07:00
|
|
|
columnWidths[k] = _minimumColumnWidth;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// see if we fit
|
2016-07-29 13:02:49 -07:00
|
|
|
int currentTableWidth = CurrentTableWidth(columnWidths);
|
|
|
|
|
int availableWidth = _tableWidth - currentTableWidth;
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
if (availableWidth < 0)
|
|
|
|
|
{
|
|
|
|
|
// if the total width is too much, we will have to remove some columns
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else if (availableWidth == 0)
|
|
|
|
|
{
|
|
|
|
|
// we just fit
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-07-29 13:02:49 -07:00
|
|
|
|
2016-03-30 14:20:52 -07:00
|
|
|
// we still have room and we want to add more width
|
2016-07-29 13:02:49 -07:00
|
|
|
|
2016-03-30 14:20:52 -07:00
|
|
|
while (availableWidth > 0)
|
|
|
|
|
{
|
|
|
|
|
for (int k = 0; k < columnWidths.Length; k++)
|
|
|
|
|
{
|
|
|
|
|
if (fixedColumn[k])
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
columnWidths[k]++;
|
|
|
|
|
availableWidth--;
|
|
|
|
|
if (availableWidth == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-12-08 18:53:30 +02:00
|
|
|
}
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
return true; // we fit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// trim columns if the total column width is too much for the screen.
|
|
|
|
|
/// </summary>
|
2018-12-28 16:39:06 +05:00
|
|
|
/// <param name="columnWidths">column widths to trim.</param>
|
2018-04-18 19:15:36 +05:00
|
|
|
private void TrimToFit(Span<int> columnWidths)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
2016-07-29 13:02:49 -07:00
|
|
|
int currentTableWidth = CurrentTableWidth(columnWidths);
|
|
|
|
|
int widthInExcess = currentTableWidth - _tableWidth;
|
2016-03-30 14:20:52 -07:00
|
|
|
if (widthInExcess <= 0)
|
|
|
|
|
{
|
|
|
|
|
return; // we are done, because we fit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we need to remove or shrink the last visible column
|
2016-07-29 13:02:49 -07:00
|
|
|
int lastVisibleColumn = GetLastVisibleColumn(columnWidths);
|
2016-03-30 14:20:52 -07:00
|
|
|
|
|
|
|
|
if (lastVisibleColumn < 0)
|
|
|
|
|
return; // nothing left to hide, because all the columns are hidden
|
|
|
|
|
|
|
|
|
|
// try to trim the last column to fit
|
|
|
|
|
int newLastVisibleColumnWidth = columnWidths[lastVisibleColumn] - widthInExcess;
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
if (newLastVisibleColumnWidth < _minimumColumnWidth)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
// cannot fit it in, just hide
|
|
|
|
|
columnWidths[lastVisibleColumn] = -1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// shrink the column to fit
|
|
|
|
|
columnWidths[lastVisibleColumn] = newLastVisibleColumnWidth;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// computes the total table width from the column width array
|
|
|
|
|
/// </summary>
|
2018-12-28 16:39:06 +05:00
|
|
|
/// <param name="columnWidths">column widths array.</param>
|
2016-03-30 14:20:52 -07:00
|
|
|
/// <returns></returns>
|
2018-04-18 19:15:36 +05:00
|
|
|
private int CurrentTableWidth(Span<int> columnWidths)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
int sum = 0;
|
|
|
|
|
int visibleColumns = 0;
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < columnWidths.Length; k++)
|
|
|
|
|
{
|
|
|
|
|
if (columnWidths[k] > 0)
|
|
|
|
|
{
|
|
|
|
|
sum += columnWidths[k];
|
|
|
|
|
visibleColumns++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
return sum + _separatorWidth * (visibleColumns - 1);
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// get the last visible column (i.e. with a width >= 0)
|
|
|
|
|
/// </summary>
|
2018-12-28 16:39:06 +05:00
|
|
|
/// <param name="columnWidths">column widths array.</param>
|
2018-12-25 21:43:03 +05:00
|
|
|
/// <returns>Index of the last visible column, -1 if none.</returns>
|
2018-04-18 19:15:36 +05:00
|
|
|
private static int GetLastVisibleColumn(Span<int> columnWidths)
|
2016-03-30 14:20:52 -07:00
|
|
|
{
|
|
|
|
|
for (int k = 0; k < columnWidths.Length; k++)
|
|
|
|
|
{
|
|
|
|
|
if (columnWidths[k] < 0)
|
|
|
|
|
return k - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return columnWidths.Length - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 13:02:49 -07:00
|
|
|
private int _tableWidth;
|
|
|
|
|
private int _minimumColumnWidth;
|
|
|
|
|
private int _separatorWidth;
|
2016-03-30 14:20:52 -07:00
|
|
|
}
|
|
|
|
|
}
|