michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #ifndef CellData_h__ michael@0: #define CellData_h__ michael@0: michael@0: #include "nsISupports.h" michael@0: #include "nsCoord.h" michael@0: #include "mozilla/gfx/Types.h" michael@0: #include michael@0: michael@0: class nsTableCellFrame; michael@0: class nsCellMap; michael@0: class BCCellData; michael@0: michael@0: michael@0: #define MAX_ROWSPAN 65534 // the cellmap can not handle more. michael@0: #define MAX_COLSPAN 1000 // limit as IE and opera do. If this ever changes, michael@0: // change COL_SPAN_OFFSET/COL_SPAN_SHIFT accordingly. michael@0: michael@0: /** michael@0: * Data stored by nsCellMap to rationalize rowspan and colspan cells. michael@0: */ michael@0: class CellData michael@0: { michael@0: public: michael@0: /** Initialize the mOrigCell pointer michael@0: * @param aOrigCell the table cell frame which will be stored in mOrigCell. michael@0: */ michael@0: void Init(nsTableCellFrame* aCellFrame); michael@0: michael@0: /** does a cell originate from here michael@0: * @return is true if a cell corresponds to this cellmap entry michael@0: */ michael@0: bool IsOrig() const; michael@0: michael@0: /** is the celldata valid michael@0: * @return is true if no cell originates and the cell is not spanned by michael@0: * a row- or colspan. mBits are 0 in this case and mOrigCell is michael@0: * nullptr michael@0: */ michael@0: bool IsDead() const; michael@0: michael@0: /** is the entry spanned by row- or a colspan michael@0: * @return is true if the entry is spanned by a row- or colspan michael@0: */ michael@0: bool IsSpan() const; michael@0: michael@0: /** is the entry spanned by rowspan michael@0: * @return is true if the entry is spanned by a rowspan michael@0: */ michael@0: bool IsRowSpan() const; michael@0: michael@0: /** is the entry spanned by a zero rowspan michael@0: * zero rowspans span all cells starting from the originating cell down to michael@0: * the end of the rowgroup or a cell originating in the same column michael@0: * @return is true if the entry is spanned by a zero rowspan michael@0: */ michael@0: bool IsZeroRowSpan() const; michael@0: michael@0: /** mark the current entry as spanned by a zero rowspan michael@0: * @param aIsZero if true mark the entry as covered by a zero rowspan michael@0: */ michael@0: void SetZeroRowSpan(bool aIsZero); michael@0: michael@0: /** get the distance from the current entry to the corresponding origin of the rowspan michael@0: * @return containing the distance in the column to the originating cell michael@0: */ michael@0: uint32_t GetRowSpanOffset() const; michael@0: michael@0: /** set the distance from the current entry to the corresponding origin of the rowspan michael@0: * @param the distance in the column to the originating cell michael@0: */ michael@0: void SetRowSpanOffset(uint32_t aSpan); michael@0: michael@0: /** is the entry spanned by colspan michael@0: * @return is true if the entry is spanned by a colspan michael@0: */ michael@0: bool IsColSpan() const; michael@0: michael@0: /** is the entry spanned by a zero colspan michael@0: * zero colspans span all cells starting from the originating cell towards michael@0: * the end of the colgroup or a cell originating in the same row michael@0: * or a rowspanned entry michael@0: * @return is true if the entry is spanned by a zero colspan michael@0: */ michael@0: bool IsZeroColSpan() const; michael@0: michael@0: /** mark the current entry as spanned by a zero colspan michael@0: * @param aIsZero if true mark the entry as covered by a zero colspan michael@0: */ michael@0: void SetZeroColSpan(bool aIsZero); michael@0: michael@0: /** get the distance from the current entry to the corresponding origin of the colspan michael@0: * @return containing the distance in the row to the originating cell michael@0: */ michael@0: uint32_t GetColSpanOffset() const; michael@0: michael@0: /** set the distance from the current entry to the corresponding origin of the colspan michael@0: * @param the distance in the column to the originating cell michael@0: */ michael@0: void SetColSpanOffset(uint32_t aSpan); michael@0: michael@0: /** is the entry spanned by a row- and a colspan michael@0: * @return is true if the entry is spanned by a row- and a colspan michael@0: */ michael@0: bool IsOverlap() const; michael@0: michael@0: /** mark the current entry as spanned by a row- and a colspan michael@0: * @param aOverlap if true mark the entry as covered by a row- and a colspan michael@0: */ michael@0: void SetOverlap(bool aOverlap); michael@0: michael@0: /** get the table cell frame for this entry michael@0: * @return a pointer to the cellframe, this will be nullptr when the entry michael@0: * is only a spanned entry michael@0: */ michael@0: nsTableCellFrame* GetCellFrame() const; michael@0: michael@0: private: michael@0: friend class nsCellMap; michael@0: friend class BCCellData; michael@0: michael@0: /** constructor. michael@0: * @param aOrigCell the table cell frame which will be stored in mOrigCell. michael@0: */ michael@0: CellData(nsTableCellFrame* aOrigCell); // implemented in nsCellMap.cpp michael@0: michael@0: /** destructor */ michael@0: ~CellData(); // implemented in nsCellMap.cpp michael@0: michael@0: protected: michael@0: michael@0: // this union relies on the assumption that an object (not primitive type) does michael@0: // not start on an odd bit boundary. If mSpan is 0 then mOrigCell is in effect michael@0: // and the data does not represent a span. If mSpan is 1, then mBits is in michael@0: // effect and the data represents a span. michael@0: // mBits must match the size of mOrigCell on both 32- and 64-bit platforms. michael@0: union { michael@0: nsTableCellFrame* mOrigCell; michael@0: uintptr_t mBits; michael@0: }; michael@0: }; michael@0: michael@0: // Border Collapsing Cell Data michael@0: enum BCBorderOwner michael@0: { michael@0: eTableOwner = 0, michael@0: eColGroupOwner = 1, michael@0: eAjaColGroupOwner = 2, // col group to the left michael@0: eColOwner = 3, michael@0: eAjaColOwner = 4, // col to the left michael@0: eRowGroupOwner = 5, michael@0: eAjaRowGroupOwner = 6, // row group above michael@0: eRowOwner = 7, michael@0: eAjaRowOwner = 8, // row above michael@0: eCellOwner = 9, michael@0: eAjaCellOwner = 10 // cell to the top or to the left michael@0: }; michael@0: michael@0: typedef uint16_t BCPixelSize; michael@0: michael@0: // These are the max sizes that are stored. If they are exceeded, then the max is stored and michael@0: // the actual value is computed when needed. michael@0: #define MAX_BORDER_WIDTH nscoord((1u << (sizeof(BCPixelSize) * 8)) - 1) michael@0: michael@0: static inline nscoord michael@0: BC_BORDER_TOP_HALF_COORD(int32_t p2t, uint16_t px) { return (px - px / 2) * p2t; } michael@0: static inline nscoord michael@0: BC_BORDER_RIGHT_HALF_COORD(int32_t p2t, uint16_t px) { return ( px / 2) * p2t; } michael@0: static inline nscoord michael@0: BC_BORDER_BOTTOM_HALF_COORD(int32_t p2t, uint16_t px) { return ( px / 2) * p2t; } michael@0: static inline nscoord michael@0: BC_BORDER_LEFT_HALF_COORD(int32_t p2t, uint16_t px) { return (px - px / 2) * p2t; } michael@0: michael@0: #define BC_BORDER_TOP_HALF(px) ((px) - (px) / 2) michael@0: #define BC_BORDER_RIGHT_HALF(px) ((px) / 2) michael@0: #define BC_BORDER_BOTTOM_HALF(px) ((px) / 2) michael@0: #define BC_BORDER_LEFT_HALF(px) ((px) - (px) / 2) michael@0: michael@0: // BCData stores the top and left border info and the corner connecting the two. michael@0: class BCData michael@0: { michael@0: public: michael@0: BCData(); michael@0: michael@0: ~BCData(); michael@0: michael@0: nscoord GetLeftEdge(BCBorderOwner& aOwner, michael@0: bool& aStart) const; michael@0: michael@0: void SetLeftEdge(BCBorderOwner aOwner, michael@0: nscoord aSize, michael@0: bool aStart); michael@0: michael@0: nscoord GetTopEdge(BCBorderOwner& aOwner, michael@0: bool& aStart) const; michael@0: michael@0: void SetTopEdge(BCBorderOwner aOwner, michael@0: nscoord aSize, michael@0: bool aStart); michael@0: michael@0: BCPixelSize GetCorner(mozilla::css::Side& aCornerOwner, michael@0: bool& aBevel) const; michael@0: michael@0: void SetCorner(BCPixelSize aSubSize, michael@0: mozilla::css::Side aOwner, michael@0: bool aBevel); michael@0: michael@0: bool IsLeftStart() const; michael@0: michael@0: void SetLeftStart(bool aValue); michael@0: michael@0: bool IsTopStart() const; michael@0: michael@0: void SetTopStart(bool aValue); michael@0: michael@0: michael@0: protected: michael@0: BCPixelSize mLeftSize; // size in pixels of left border michael@0: BCPixelSize mTopSize; // size in pixels of top border michael@0: BCPixelSize mCornerSubSize; // size of the largest border not in the michael@0: // dominant plane (for example, if corner is michael@0: // owned by the segment to its top or bottom, michael@0: // then the size is the max of the border michael@0: // sizes of the segments to its left or right. michael@0: unsigned mLeftOwner: 4; // owner of left border michael@0: unsigned mTopOwner: 4; // owner of top border michael@0: unsigned mLeftStart: 1; // set if this is the start of a vertical border segment michael@0: unsigned mTopStart: 1; // set if this is the start of a horizontal border segment michael@0: unsigned mCornerSide: 2; // mozilla::css::Side of the owner of the upper left corner relative to the corner michael@0: unsigned mCornerBevel: 1; // is the corner beveled (only two segments, perpendicular, not dashed or dotted). michael@0: }; michael@0: michael@0: // BCCellData entries replace CellData entries in the cell map if the border collapsing model is in michael@0: // effect. BCData for a row and col entry contains the left and top borders of cell at that row and michael@0: // col and the corner connecting the two. The right borders of the cells in the last col and the bottom michael@0: // borders of the last row are stored in separate BCData entries in the cell map. michael@0: class BCCellData : public CellData michael@0: { michael@0: public: michael@0: BCCellData(nsTableCellFrame* aOrigCell); michael@0: ~BCCellData(); michael@0: michael@0: BCData mData; michael@0: }; michael@0: michael@0: michael@0: // The layout of a celldata is as follows. The top 10 bits are the colspan michael@0: // offset (which is enough to represent our allowed values 1-1000 for colspan). michael@0: // Then there are three bits of flags. Then 16 bits of rowspan offset (which michael@0: // lets us represent numbers up to 65535. Then another 3 bits of flags. michael@0: michael@0: // num bits to shift right to get right aligned col span michael@0: #define COL_SPAN_SHIFT 22 michael@0: // num bits to shift right to get right aligned row span michael@0: #define ROW_SPAN_SHIFT 3 michael@0: michael@0: // the col offset to the data containing the original cell. michael@0: #define COL_SPAN_OFFSET (0x3FF << COL_SPAN_SHIFT) michael@0: // the row offset to the data containing the original cell michael@0: #define ROW_SPAN_OFFSET (0xFFFF << ROW_SPAN_SHIFT) michael@0: michael@0: // And the flags michael@0: #define SPAN 0x00000001 // there a row or col span michael@0: #define ROW_SPAN 0x00000002 // there is a row span michael@0: #define ROW_SPAN_0 0x00000004 // the row span is 0 michael@0: #define COL_SPAN (1 << (COL_SPAN_SHIFT - 3)) // there is a col span michael@0: #define COL_SPAN_0 (1 << (COL_SPAN_SHIFT - 2)) // the col span is 0 michael@0: #define OVERLAP (1 << (COL_SPAN_SHIFT - 1)) // there is a row span and michael@0: // col span but not by michael@0: // same cell michael@0: michael@0: inline nsTableCellFrame* CellData::GetCellFrame() const michael@0: { michael@0: if (SPAN != (SPAN & mBits)) { michael@0: return mOrigCell; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: inline void CellData::Init(nsTableCellFrame* aCellFrame) michael@0: { michael@0: mOrigCell = aCellFrame; michael@0: } michael@0: michael@0: inline bool CellData::IsOrig() const michael@0: { michael@0: return ((nullptr != mOrigCell) && (SPAN != (SPAN & mBits))); michael@0: } michael@0: michael@0: inline bool CellData::IsDead() const michael@0: { michael@0: return (0 == mBits); michael@0: } michael@0: michael@0: inline bool CellData::IsSpan() const michael@0: { michael@0: return (SPAN == (SPAN & mBits)); michael@0: } michael@0: michael@0: inline bool CellData::IsRowSpan() const michael@0: { michael@0: return (SPAN == (SPAN & mBits)) && michael@0: (ROW_SPAN == (ROW_SPAN & mBits)); michael@0: } michael@0: michael@0: inline bool CellData::IsZeroRowSpan() const michael@0: { michael@0: return (SPAN == (SPAN & mBits)) && michael@0: (ROW_SPAN == (ROW_SPAN & mBits)) && michael@0: (ROW_SPAN_0 == (ROW_SPAN_0 & mBits)); michael@0: } michael@0: michael@0: inline void CellData::SetZeroRowSpan(bool aIsZeroSpan) michael@0: { michael@0: if (SPAN == (SPAN & mBits)) { michael@0: if (aIsZeroSpan) { michael@0: mBits |= ROW_SPAN_0; michael@0: } michael@0: else { michael@0: mBits &= ~ROW_SPAN_0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: inline uint32_t CellData::GetRowSpanOffset() const michael@0: { michael@0: if ((SPAN == (SPAN & mBits)) && ((ROW_SPAN == (ROW_SPAN & mBits)))) { michael@0: return (uint32_t)((mBits & ROW_SPAN_OFFSET) >> ROW_SPAN_SHIFT); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: inline void CellData::SetRowSpanOffset(uint32_t aSpan) michael@0: { michael@0: mBits &= ~ROW_SPAN_OFFSET; michael@0: mBits |= (aSpan << ROW_SPAN_SHIFT); michael@0: mBits |= SPAN; michael@0: mBits |= ROW_SPAN; michael@0: } michael@0: michael@0: inline bool CellData::IsColSpan() const michael@0: { michael@0: return (SPAN == (SPAN & mBits)) && michael@0: (COL_SPAN == (COL_SPAN & mBits)); michael@0: } michael@0: michael@0: inline bool CellData::IsZeroColSpan() const michael@0: { michael@0: return (SPAN == (SPAN & mBits)) && michael@0: (COL_SPAN == (COL_SPAN & mBits)) && michael@0: (COL_SPAN_0 == (COL_SPAN_0 & mBits)); michael@0: } michael@0: michael@0: inline void CellData::SetZeroColSpan(bool aIsZeroSpan) michael@0: { michael@0: if (SPAN == (SPAN & mBits)) { michael@0: if (aIsZeroSpan) { michael@0: mBits |= COL_SPAN_0; michael@0: } michael@0: else { michael@0: mBits &= ~COL_SPAN_0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: inline uint32_t CellData::GetColSpanOffset() const michael@0: { michael@0: if ((SPAN == (SPAN & mBits)) && ((COL_SPAN == (COL_SPAN & mBits)))) { michael@0: return (uint32_t)((mBits & COL_SPAN_OFFSET) >> COL_SPAN_SHIFT); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: inline void CellData::SetColSpanOffset(uint32_t aSpan) michael@0: { michael@0: mBits &= ~COL_SPAN_OFFSET; michael@0: mBits |= (aSpan << COL_SPAN_SHIFT); michael@0: michael@0: mBits |= SPAN; michael@0: mBits |= COL_SPAN; michael@0: } michael@0: michael@0: inline bool CellData::IsOverlap() const michael@0: { michael@0: return (SPAN == (SPAN & mBits)) && (OVERLAP == (OVERLAP & mBits)); michael@0: } michael@0: michael@0: inline void CellData::SetOverlap(bool aOverlap) michael@0: { michael@0: if (SPAN == (SPAN & mBits)) { michael@0: if (aOverlap) { michael@0: mBits |= OVERLAP; michael@0: } michael@0: else { michael@0: mBits &= ~OVERLAP; michael@0: } michael@0: } michael@0: } michael@0: michael@0: inline BCData::BCData() michael@0: { michael@0: mLeftOwner = mTopOwner = eCellOwner; michael@0: mLeftStart = mTopStart = 1; michael@0: mLeftSize = mCornerSubSize = mTopSize = 0; michael@0: mCornerSide = NS_SIDE_TOP; michael@0: mCornerBevel = false; michael@0: } michael@0: michael@0: inline BCData::~BCData() michael@0: { michael@0: } michael@0: michael@0: inline nscoord BCData::GetLeftEdge(BCBorderOwner& aOwner, michael@0: bool& aStart) const michael@0: { michael@0: aOwner = (BCBorderOwner)mLeftOwner; michael@0: aStart = (bool)mLeftStart; michael@0: michael@0: return (nscoord)mLeftSize; michael@0: } michael@0: michael@0: inline void BCData::SetLeftEdge(BCBorderOwner aOwner, michael@0: nscoord aSize, michael@0: bool aStart) michael@0: { michael@0: mLeftOwner = aOwner; michael@0: mLeftSize = (aSize > MAX_BORDER_WIDTH) ? MAX_BORDER_WIDTH : aSize; michael@0: mLeftStart = aStart; michael@0: } michael@0: michael@0: inline nscoord BCData::GetTopEdge(BCBorderOwner& aOwner, michael@0: bool& aStart) const michael@0: { michael@0: aOwner = (BCBorderOwner)mTopOwner; michael@0: aStart = (bool)mTopStart; michael@0: michael@0: return (nscoord)mTopSize; michael@0: } michael@0: michael@0: inline void BCData::SetTopEdge(BCBorderOwner aOwner, michael@0: nscoord aSize, michael@0: bool aStart) michael@0: { michael@0: mTopOwner = aOwner; michael@0: mTopSize = (aSize > MAX_BORDER_WIDTH) ? MAX_BORDER_WIDTH : aSize; michael@0: mTopStart = aStart; michael@0: } michael@0: michael@0: inline BCPixelSize BCData::GetCorner(mozilla::css::Side& aOwnerSide, michael@0: bool& aBevel) const michael@0: { michael@0: aOwnerSide = mozilla::css::Side(mCornerSide); michael@0: aBevel = (bool)mCornerBevel; michael@0: return mCornerSubSize; michael@0: } michael@0: michael@0: inline void BCData::SetCorner(BCPixelSize aSubSize, michael@0: mozilla::css::Side aOwnerSide, michael@0: bool aBevel) michael@0: { michael@0: mCornerSubSize = aSubSize; michael@0: mCornerSide = aOwnerSide; michael@0: mCornerBevel = aBevel; michael@0: } michael@0: michael@0: inline bool BCData::IsLeftStart() const michael@0: { michael@0: return (bool)mLeftStart; michael@0: } michael@0: michael@0: inline void BCData::SetLeftStart(bool aValue) michael@0: { michael@0: mLeftStart = aValue; michael@0: } michael@0: michael@0: inline bool BCData::IsTopStart() const michael@0: { michael@0: return (bool)mTopStart; michael@0: } michael@0: michael@0: inline void BCData::SetTopStart(bool aValue) michael@0: { michael@0: mTopStart = aValue; michael@0: } michael@0: michael@0: #endif