Implement wxGridBlockCoords class

wxGridBlockCoords represents a location of a block of cells in the grid.
This commit is contained in:
Ilya Sinitsyn
2020-02-28 02:38:03 +07:00
committed by Vadim Zeitlin
parent 673ed29d7b
commit acd72efbf1
4 changed files with 722 additions and 2 deletions

View File

@@ -747,9 +747,132 @@ private:
}; };
// ----------------------------------------------------------------------------
// wxGridBlockCoords: location of a block of cells in the grid
// ----------------------------------------------------------------------------
struct wxGridBlockDiffResult;
class WXDLLIMPEXP_CORE wxGridBlockCoords
{
public:
wxGridBlockCoords() :
m_topRow(-1),
m_leftCol(-1),
m_bottomRow(-1),
m_rightCol(-1)
{
}
wxGridBlockCoords(int topRow, int leftCol, int bottomRow, int rightCol) :
m_topRow(topRow),
m_leftCol(leftCol),
m_bottomRow(bottomRow),
m_rightCol(rightCol)
{
}
// default copy ctor is ok
int GetTopRow() const { return m_topRow; }
void SetTopRow(int row) { m_topRow = row; }
int GetLeftCol() const { return m_leftCol; }
void SetLeftCol(int col) { m_leftCol = col; }
int GetBottomRow() const { return m_bottomRow; }
void SetBottomRow(int row) { m_bottomRow = row; }
int GetRightCol() const { return m_rightCol; }
void SetRightCol(int col) { m_rightCol = col; }
wxGridCellCoords GetTopLeft() const
{
return wxGridCellCoords(m_topRow, m_leftCol);
}
wxGridCellCoords GetBottomRight() const
{
return wxGridCellCoords(m_bottomRow, m_rightCol);
}
wxGridBlockCoords Canonicalize() const
{
wxGridBlockCoords result = *this;
if ( result.m_topRow > result.m_bottomRow )
wxSwap(result.m_topRow, result.m_bottomRow);
if ( result.m_leftCol > result.m_rightCol )
wxSwap(result.m_leftCol, result.m_rightCol);
return result;
}
bool Intersects(const wxGridBlockCoords& other) const
{
return m_topRow <= other.m_bottomRow && m_bottomRow >= other.m_topRow &&
m_leftCol <= other.m_rightCol && m_rightCol >= other.m_leftCol;
}
// Whether the block contains the cell.
// returns @true, if the block contains the cell,
// @false, otherwise
bool ContainCell(const wxGridCellCoords& cell) const;
// Whether the blocks contain each other.
// returns 1, if this block contains the other,
// -1, if the other block contains this one,
// 0, otherwise
int ContainBlock(const wxGridBlockCoords& other) const;
// Calculates the result blocks by subtracting the other block from this
// block. splitOrientation can be wxVERTICAL or wxHORIZONTAL.
wxGridBlockDiffResult
Difference(const wxGridBlockCoords& other, int splitOrientation) const;
// Calculates the symmetric difference of the blocks.
wxGridBlockDiffResult
SymDifference(const wxGridBlockCoords& other) const;
bool operator==(const wxGridBlockCoords& other) const
{
return m_topRow == other.m_topRow && m_leftCol == other.m_leftCol &&
m_bottomRow == other.m_bottomRow && m_rightCol == other.m_rightCol;
}
bool operator!=(const wxGridBlockCoords& other) const
{
return !(*this == other);
}
bool operator!() const
{
return m_topRow == -1 && m_leftCol == -1 &&
m_bottomRow == -1 && m_rightCol == -1;
}
private:
int m_topRow;
int m_leftCol;
int m_bottomRow;
int m_rightCol;
};
// ----------------------------------------------------------------------------
// wxGridBlockDiffResult: The helper struct uses as a result type for difference
// functions of wxGridBlockCoords class.
// Parts can be uninitialized (equals to wxGridNoBlockCoords), that means
// that the corresponding part doesn't exists in the result.
// ----------------------------------------------------------------------------
struct wxGridBlockDiffResult
{
wxGridBlockCoords m_parts[4];
};
// For comparisons... // For comparisons...
// //
extern WXDLLIMPEXP_CORE wxGridCellCoords wxGridNoCellCoords; extern WXDLLIMPEXP_CORE wxGridCellCoords wxGridNoCellCoords;
extern WXDLLIMPEXP_CORE wxGridBlockCoords wxGridNoBlockCoords;
extern WXDLLIMPEXP_CORE wxRect wxGridNoCellRect; extern WXDLLIMPEXP_CORE wxRect wxGridNoCellRect;
// An array of cell coords... // An array of cell coords...

View File

@@ -1816,6 +1816,195 @@ public:
bool operator!() const; bool operator!() const;
}; };
/**
Represents coordinates of a block of cells in the grid.
An object of this class contains coordinates of the left top and the bottom right
corners of a block.
@since 3.1.4
*/
class wxGridBlockCoords
{
public:
/**
Default constructor initializes the object to invalid state.
Initially the coordinates are invalid (-1) and so operator!() for an
uninitialized wxGridBlockCoords returns @true.
*/
wxGridBlockCoords();
/**
Constructor taking a coordinates of the left top and the bottom right
corners.
*/
wxGridBlockCoords(int topRow, int leftCol, int bottomRow, int rightCol);
/**
Return the row of the left top corner.
*/
int GetTopRow() const;
/**
Set the row of the left top corner.
*/
void SetTopRow(int row);
/**
Return the column of the left top corner.
*/
int GetLeftCol() const;
/**
Set the column of the left top corner.
*/
void SetLeftCol(int col);
/**
Return the row of the bottom right corner.
*/
int GetBottomRow() const;
/**
Set the row of the bottom right corner.
*/
void SetBottomRow(int row);
/**
Return the column of the bottom right corner.
*/
int GetRightCol() const;
/**
Set the column of the bottom right corner.
*/
void SetRightCol(int col);
/**
Return the coordinates of the top left corner.
*/
wxGridCellCoords GetTopLeft() const
{
return wxGridCellCoords(m_topRow, m_leftCol);
}
/**
Return the coordinates of the bottom right corner.
*/
wxGridCellCoords GetBottomRight() const
{
return wxGridCellCoords(m_bottomRow, m_rightCol);
}
/**
Return the canonicalized block where top left coordinates is less
then bottom right coordinates.
*/
wxGridBlockCoords Canonicalize() const;
/**
Whether the blocks intersects.
@return
@true, if the block intersects with the other, @false, otherwise.
*/
bool Intersects(const wxGridBlockCoords& other) const
{
return m_topRow <= other.m_bottomRow && m_bottomRow >= other.m_topRow &&
m_leftCol <= other.m_rightCol && m_rightCol >= other.m_leftCol;
}
/**
Whether the block contains the cell.
@return
@true, if the block contains the cell, @false, otherwise.
*/
bool ContainCell(const wxGridCellCoords& cell) const;
/**
Whether the blocks contain each other.
@return
1, if this block contains the other,
-1, if the other block contains this one,
0, otherwise.
*/
int ContainBlock(const wxGridBlockCoords& other) const;
/**
Calculates the result blocks by subtracting the other block from this
block.
@param other
The block to subtract from this block.
@param splitOrientation
The block splitting orientation (either @c wxHORIZONTAL or
@c wxVERTICAL).
@return
Up to 4 blocks. If block doesn't exist in the result, it has value
of @c wxGridNoBlockCoords.
*/
wxGridBlockDiffResult
Difference(const wxGridBlockCoords& other, int splitOrientation) const;
/**
Calculates the symmetric difference of the blocks.
@param other
The block to subtract from this block.
@return
Up to 4 blocks. If block doesn't exist in the result, it has value
of @c wxGridNoBlockCoords.
*/
wxGridBlockDiffResult
SymDifference(const wxGridBlockCoords& other) const;
/**
Equality operator.
*/
bool operator==(const wxGridBlockCoords& other) const;
/**
Inequality operator.
*/
bool operator!=(const wxGridBlockCoords& other) const;
/**
Checks whether the cells block is invalid.
Returns @true only if all components are -1. Notice that if one
of the components (but not all) is -1, this method returns @false even
if the object is invalid. This is done because objects in such state
should actually never exist, i.e. either all components should be -1
or none of them should be -1.
*/
bool operator!() const;
private:
int m_topRow;
int m_leftCol;
int m_bottomRow;
int m_rightCol;
};
/**
@class wxGridBlockDiffResult
The helper struct uses as a result type for difference functions of
@c wxGridBlockCoords class.
Parts can be uninitialized (equals to @c wxGridNoBlockCoords), that means
that the corresponding part doesn't exists in the result.
@since 3.1.4
*/
struct wxGridBlockDiffResult
{
wxGridBlockCoords m_parts[4];
};
/** /**
@class wxGridTableBase @class wxGridTableBase

View File

@@ -94,6 +94,7 @@ struct DefaultHeaderRenderers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
wxGridCellCoords wxGridNoCellCoords( -1, -1 ); wxGridCellCoords wxGridNoCellCoords( -1, -1 );
wxGridBlockCoords wxGridNoBlockCoords( -1, -1, -1, -1 );
wxRect wxGridNoCellRect( -1, -1, -1, -1 ); wxRect wxGridNoCellRect( -1, -1, -1, -1 );
namespace namespace
@@ -1152,6 +1153,226 @@ const wxGridCornerHeaderRenderer& wxGridCellAttrProvider::GetCornerRenderer()
return gs_defaultHeaderRenderers.cornerRenderer; return gs_defaultHeaderRenderers.cornerRenderer;
} }
// ----------------------------------------------------------------------------
// wxGridBlockCoords
// ----------------------------------------------------------------------------
bool wxGridBlockCoords::ContainCell(const wxGridCellCoords& cell) const
{
return m_topRow <= cell.GetRow() && cell.GetRow() <= m_bottomRow &&
m_leftCol <= cell.GetCol() && cell.GetCol() <= m_rightCol;
}
int wxGridBlockCoords::ContainBlock(const wxGridBlockCoords& other) const
{
// returns 1, if this block contains the other,
// -1, if the other block contains this one,
// 0, otherwise
if ( m_topRow <= other.m_topRow && other.m_bottomRow <= m_bottomRow &&
m_leftCol <= other.m_leftCol && other.m_rightCol <= m_rightCol )
return 1;
else if ( other.m_topRow <= m_topRow && m_bottomRow <= other.m_bottomRow &&
other.m_leftCol <= m_leftCol && m_rightCol <= other.m_rightCol )
return -1;
return 0;
}
wxGridBlockDiffResult
wxGridBlockCoords::Difference(const wxGridBlockCoords& other,
int splitOrientation) const
{
wxGridBlockDiffResult result;
// Check whether the blocks intersect.
if (!Intersects(other))
{
result.m_parts[0] = *this;
return result;
}
// Split the block in up to 4 new parts, that don't contain the other
// block, like this (for wxHORIZONTAL):
// |-----------------------------|
// | |
// | part[0] |
// | |
// |-----------------------------|
// | part[2] | other | part[3] |
// |-----------------------------|
// | |
// | part[1] |
// | |
// |-----------------------------|
// And for wxVERTICAL:
// |-----------------------------|
// | | | |
// | | part[2] | |
// | | | |
// | |---------| |
// | part[0] | other | part[1] |
// | |---------| |
// | | | |
// | | part[3] | |
// | | | |
// |-----------------------------|
if ( splitOrientation == wxHORIZONTAL )
{
// Part[0].
if ( m_topRow < other.m_topRow )
result.m_parts[0] =
wxGridBlockCoords(m_topRow, m_leftCol,
other.m_topRow - 1, m_rightCol);
// Part[1].
if ( m_bottomRow > other.m_bottomRow )
result.m_parts[1] =
wxGridBlockCoords(other.m_bottomRow + 1, m_leftCol,
m_bottomRow, m_rightCol);
const int maxTopRow = wxMax(m_topRow, other.m_topRow);
const int minBottomRow = wxMin(m_bottomRow, other.m_bottomRow);
// Part[2].
if ( m_leftCol < other.m_leftCol )
result.m_parts[2] =
wxGridBlockCoords(maxTopRow, m_leftCol,
minBottomRow, other.m_leftCol - 1);
// Part[3].
if ( m_rightCol > other.m_rightCol )
result.m_parts[3] =
wxGridBlockCoords(maxTopRow, other.m_rightCol + 1,
minBottomRow, m_rightCol);
}
else // wxVERTICAL
{
// Part[0].
if ( m_leftCol < other.m_leftCol )
result.m_parts[0] =
wxGridBlockCoords(m_topRow, m_leftCol,
m_bottomRow, other.m_leftCol - 1);
// Part[1].
if ( m_rightCol > other.m_rightCol )
result.m_parts[1] =
wxGridBlockCoords(m_topRow, other.m_rightCol + 1,
m_bottomRow, m_rightCol);
const int maxLeftCol = wxMax(m_leftCol, other.m_leftCol);
const int minRightCol = wxMin(m_rightCol, other.m_rightCol);
// Part[2].
if ( m_topRow < other.m_topRow )
result.m_parts[2] =
wxGridBlockCoords(m_topRow, maxLeftCol,
other.m_topRow - 1, minRightCol);
// Part[3].
if ( m_bottomRow > other.m_bottomRow )
result.m_parts[3] =
wxGridBlockCoords(other.m_bottomRow + 1, maxLeftCol,
m_bottomRow, minRightCol);
}
return result;
}
wxGridBlockDiffResult
wxGridBlockCoords::SymDifference(const wxGridBlockCoords& other) const
{
wxGridBlockDiffResult result;
// Check whether the blocks intersect.
if ( !Intersects(other) )
{
result.m_parts[0] = *this;
result.m_parts[1] = other;
return result;
}
// Possible result blocks:
// |------------------|
// | | minUpper->m_topRow
// | part[0] |
// | | maxUpper->m_topRow - 1
// |-----------------------------|
// | | | | maxUpper->m_topRow
// | part[2] | x | part[3] |
// | | | | minLower->m_bottomRow
// |-----------------------------|
// | | minLower->m_bottomRow + 1
// | part[1] |
// | | maxLower->m_bottomRow
// |-------------------|
//
// The x marks the intersection of the blocks, which is not a part
// of the result.
// Part[0].
int maxUpperRow;
if ( m_topRow != other.m_topRow )
{
const bool block1Min = m_topRow < other.m_topRow;
const wxGridBlockCoords& minUpper = block1Min ? *this : other;
const wxGridBlockCoords& maxUpper = block1Min ? other : *this;
maxUpperRow = maxUpper.m_topRow;
result.m_parts[0] = wxGridBlockCoords(minUpper.m_topRow,
minUpper.m_leftCol,
maxUpper.m_topRow - 1,
minUpper.m_rightCol);
}
else
{
maxUpperRow = m_topRow;
}
// Part[1].
int minLowerRow;
if ( m_bottomRow != other.m_bottomRow )
{
const bool block1Min = m_bottomRow < other.m_bottomRow;
const wxGridBlockCoords& minLower = block1Min ? *this : other;
const wxGridBlockCoords& maxLower = block1Min ? other : *this;
minLowerRow = minLower.m_bottomRow;
result.m_parts[1] = wxGridBlockCoords(minLower.m_bottomRow + 1,
maxLower.m_leftCol,
maxLower.m_bottomRow,
maxLower.m_rightCol);
}
else
{
minLowerRow = m_bottomRow;
}
// Part[2].
if ( m_leftCol != other.m_leftCol )
{
result.m_parts[2] = wxGridBlockCoords(maxUpperRow,
wxMin(m_leftCol,
other.m_leftCol),
minLowerRow,
wxMax(m_leftCol,
other.m_leftCol) - 1);
}
// Part[3].
if ( m_rightCol != other.m_rightCol )
{
result.m_parts[3] = wxGridBlockCoords(maxUpperRow,
wxMin(m_rightCol,
other.m_rightCol) + 1,
minLowerRow,
wxMax(m_rightCol,
other.m_rightCol));
}
return result;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxGridTableBase // wxGridTableBase
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@@ -1259,4 +1259,191 @@ TEST_CASE_METHOD(GridTestCase, "Grid::AutoSizeColumn", "[grid]")
} }
} }
// Test wxGridBlockCoords here because it'a a part of grid sources.
std::ostream& operator<<(std::ostream& os, const wxGridBlockCoords& block) {
os << "wxGridBlockCoords(" <<
block.GetTopRow() << ", " << block.GetLeftCol() << ", " <<
block.GetBottomRow() << ", " << block.GetRightCol() << ")";
return os;
}
TEST_CASE("GridBlockCoords::Canonicalize", "[grid]")
{
const wxGridBlockCoords block =
wxGridBlockCoords(4, 3, 2, 1).Canonicalize();
CHECK(block.GetTopRow() == 2);
CHECK(block.GetLeftCol() == 1);
CHECK(block.GetBottomRow() == 4);
CHECK(block.GetRightCol() == 3);
}
TEST_CASE("GridBlockCoords::Intersects", "[grid]")
{
// Inside.
CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(1, 2, 2, 3)));
// Intersects.
CHECK(wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(2, 2, 4, 4)));
// Doesn't intersects.
CHECK(!wxGridBlockCoords(1, 1, 3, 3).Intersects(wxGridBlockCoords(4, 4, 6, 6)));
}
TEST_CASE("GridBlockCoords::ContainCell", "[grid]")
{
// Inside.
CHECK(wxGridBlockCoords(1, 1, 3, 3).ContainCell(wxGridCellCoords(2, 2)));
// Outside.
CHECK(!wxGridBlockCoords(1, 1, 3, 3).ContainCell(wxGridCellCoords(5, 5)));
}
TEST_CASE("GridBlockCoords::ContainBlock", "[grid]")
{
wxGridBlockCoords block1(1, 1, 5, 5);
wxGridBlockCoords block2(1, 1, 3, 3);
wxGridBlockCoords block3(2, 2, 7, 7);
wxGridBlockCoords block4(10, 10, 12, 12);
CHECK(block1.ContainBlock(block2) == 1);
CHECK(block2.ContainBlock(block1) == -1);
CHECK(block1.ContainBlock(block3) == 0);
CHECK(block1.ContainBlock(block4) == 0);
}
TEST_CASE("GridBlockCoords::Difference", "[grid]")
{
SECTION("Subtract contained block (splitted horizontally)")
{
const wxGridBlockCoords block1(1, 1, 7, 7);
const wxGridBlockCoords block2(3, 3, 5, 5);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
}
SECTION("Subtract contained block (splitted vertically)")
{
const wxGridBlockCoords block1(1, 1, 7, 7);
const wxGridBlockCoords block2(3, 3, 5, 5);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxVERTICAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 7, 2));
CHECK(result.m_parts[1] == wxGridBlockCoords(1, 6, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
CHECK(result.m_parts[3] == wxGridBlockCoords(6, 3, 7, 5));
}
SECTION("Blocks intersect by the corner (splitted horizontally)")
{
const wxGridBlockCoords block1(1, 1, 5, 5);
const wxGridBlockCoords block2(3, 3, 7, 7);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks intersect by the corner (splitted vertically)")
{
const wxGridBlockCoords block1(1, 1, 5, 5);
const wxGridBlockCoords block2(3, 3, 7, 7);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxVERTICAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 5, 2));
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridBlockCoords(1, 3, 2, 5));
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks are the same")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(1, 1, 3, 3);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridNoBlockCoords);
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks doesn't intersects")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(5, 5, 7, 7);
const wxGridBlockDiffResult result =
block1.Difference(block2, wxHORIZONTAL);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
}
TEST_CASE("GridBlockCoords::SymDifference", "[grid]")
{
SECTION("With contained block")
{
const wxGridBlockCoords block1(1, 1, 7, 7);
const wxGridBlockCoords block2(3, 3, 5, 5);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 7));
CHECK(result.m_parts[1] == wxGridBlockCoords(6, 1, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
}
SECTION("Blocks intersect by the corner")
{
const wxGridBlockCoords block1(1, 1, 5, 5);
const wxGridBlockCoords block2(3, 3, 7, 7);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 2, 5));
CHECK(result.m_parts[1] == wxGridBlockCoords(6, 3, 7, 7));
CHECK(result.m_parts[2] == wxGridBlockCoords(3, 1, 5, 2));
CHECK(result.m_parts[3] == wxGridBlockCoords(3, 6, 5, 7));
}
SECTION("Blocks are the same")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(1, 1, 3, 3);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridNoBlockCoords);
CHECK(result.m_parts[1] == wxGridNoBlockCoords);
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
SECTION("Blocks doesn't intersects")
{
const wxGridBlockCoords block1(1, 1, 3, 3);
const wxGridBlockCoords block2(5, 5, 7, 7);
const wxGridBlockDiffResult result = block1.SymDifference(block2);
CHECK(result.m_parts[0] == wxGridBlockCoords(1, 1, 3, 3));
CHECK(result.m_parts[1] == wxGridBlockCoords(5, 5, 7, 7));
CHECK(result.m_parts[2] == wxGridNoBlockCoords);
CHECK(result.m_parts[3] == wxGridNoBlockCoords);
}
}
#endif //wxUSE_GRID #endif //wxUSE_GRID