// $Revision: 1.23 $ // Copyright (C) 1994 Taligent, Inc. All rights reserved. #ifndef Taligent_LAYOUTVIEWS #include "LayoutViews.h" #endif #ifndef Taligent_GRAFPORT #include #endif #ifndef Taligent_NUMERICS #include #endif #ifndef Taligent_VIEWREQUEST #include #endif //======================================================================== // class declaration: TLayoutChildrenRequest // // A functor/request that can be posted to call a method in a layout view's // thread. //======================================================================== typedef void (TLayoutView::* LayoutChildrenMethod)(); class TLayoutChildrenRequest : public TViewRequest { public: TLayoutChildrenRequest( const TLayoutView& view, LayoutChildrenMethod method); virtual ~TLayoutChildrenRequest(); TLayoutChildrenRequest( const TLayoutChildrenRequest& ); TLayoutChildrenRequest& operator=( const TLayoutChildrenRequest& ); virtual void Do(); private: LayoutChildrenMethod fMethod; }; TLayoutChildrenRequest::TLayoutChildrenRequest( const TLayoutView& view, LayoutChildrenMethod method ) : TViewRequest( new (TAllocationHeap(this)) TViewHandle(view) ), fMethod( method ) { } TLayoutChildrenRequest::TLayoutChildrenRequest( const TLayoutChildrenRequest& other ) : TViewRequest(other), fMethod( other.fMethod ) { } TLayoutChildrenRequest& TLayoutChildrenRequest::operator=( const TLayoutChildrenRequest& other ) { TViewRequest::operator=( other ); fMethod = other.fMethod; return *this; } TLayoutChildrenRequest::~TLayoutChildrenRequest() { } void TLayoutChildrenRequest::Do() { TLayoutView* view = (TLayoutView*)GetView(); if (view) { (view->*fMethod)(); } } //---- TLayoutView ------------------------------------------------------------- TLayoutView::~TLayoutView() { CheckForFinalizeNoInit(&gMetaInfo); } long TLayoutView::Count() const { long i= 0; TView *v; TViewChildIterator iter(*this); for (v= iter.First(); v; v= iter.Next()) i++; return i; } TView* TLayoutView::GetChild(long a) const { if (a < 0 || a >= Count()) return 0; TView *v; TViewChildIterator iter(*this); for (v= iter.First(); v; v= iter.Next(), a--) if (a == 0) break; return v; } void TLayoutView::DrawContents(TGrafPort& port) const { } bool TLayoutView::NeedsClipping() { return false; } void TLayoutView::GetOpaquelyDrawnArea(TGArea& opaqueArea) { opaqueArea.SetToEmpty(); } void TLayoutView::HandleAllocatedAreaChanged(const TGArea& area) { TView::HandleAllocatedAreaChanged( area ); // This operation invalidates the layout, so post a request only if none has been posted already. if (fLayoutValid) { fLayoutValid = false; PostLayoutRequest(); } } void TLayoutView::HandleAfterConnectionToViewRoot() { TView::HandleAfterConnectionToViewRoot(); // Post a request if layout is invalid because up until this point, this view was disconnected // from the view root which prevented previous layout requests from posting successfully. if (!fLayoutValid) PostLayoutRequest(); } void TLayoutView::HandleNumberofChildrenChanged() { TView::HandleNumberofChildrenChanged(); // This operation invalidates the layout, so post a request only if none has been posted already. if (fLayoutValid) { fLayoutValid = false; PostLayoutRequest(); } } void TLayoutView::PlaceAndSizeChildren(const TGRect& area) { } void TLayoutView::DoLayout() { if (!fLayoutValid) { TGArea theArea; TView::GetAllocatedArea(theArea); PlaceAndSizeChildren(theArea.GetBounds()); fLayoutValid = true; } } void TLayoutView::PostLayoutRequest() { TRequestProcessor* proc = GetRequestProcessor(); if (proc != NIL) proc->AdoptRequest(new TLayoutChildrenRequest(*this, &TLayoutView::DoLayout), TRequestProcessor::kHigh); } void TLayoutView::GetBounds(TGArea& area) const { GetAllocatedArea(area); } // Miss Manners stuff VersionDefinitionsMacro(TLayoutView, 0); TLayoutView::TLayoutView() : VViewInitialize(&gMetaInfo), fLayoutValid(true) { CheckForInitializeNoInit(&gMetaInfo); } TLayoutView::TLayoutView(const TLayoutView &other) : VViewInitialize(&gMetaInfo), TView(other), fLayoutValid(true) { CheckForInitializeNoInit(&gMetaInfo); } TLayoutView& TLayoutView::operator=(const TLayoutView& other) { if (&other != this) { TView::operator=(other); fLayoutValid = other.fLayoutValid; } return *this; } //---- TBoxView -------------------------------------------------------------------- const GCoordinate kDefaultGap= 4.0; TBoxView::TBoxView(long c, long r, TView* v1, TView* v2, TView* v3, TView* v4) : VViewInitialize(&gMetaInfo), fCols(c), fRows(r), fGap(kDefaultGap, kDefaultGap), fHAlign(kLeft), fVAlign(kTop), fHExpand(kNoExpand), fVExpand(kNoExpand) { AdoptChildren(v1, v2, v3, v4); CheckForInitializeNoInit(&gMetaInfo); } TBoxView::TBoxView(long c, long r, EAlign ha, EAlign va, TView* v1, TView* v2, TView* v3, TView* v4) : VViewInitialize(&gMetaInfo), fCols(c), fRows(r), fGap(kDefaultGap, kDefaultGap), fHAlign(ha), fVAlign(va), fHExpand(kNoExpand), fVExpand(kNoExpand) { AdoptChildren(v1, v2, v3, v4); CheckForInitializeNoInit(&gMetaInfo); } TBoxView::TBoxView(long c, long r, const TGPoint &gap, TView* v1, TView* v2, TView* v3, TView* v4) : VViewInitialize(&gMetaInfo), fCols(c), fRows(r), fGap(gap), fHAlign(kLeft), fVAlign(kTop), fHExpand(kNoExpand), fVExpand(kNoExpand) { AdoptChildren(v1, v2, v3, v4); CheckForInitializeNoInit(&gMetaInfo); } TBoxView::TBoxView(long c, long r, EAlign ha, EAlign va, EExpand he, EExpand ve, const TGPoint &gap) : VViewInitialize(&gMetaInfo), fCols(c), fRows(r), fGap(gap), fHAlign(ha), fVAlign(va), fHExpand(he), fVExpand(ve) { CheckForInitializeNoInit(&gMetaInfo); } void TBoxView::AdoptChildren(TView* v1, TView* v2, TView* v3, TView* v4) { if (v1) AdoptChild(v1); if (v2) AdoptChild(v2); if (v3) AdoptChild(v3); if (v4) AdoptChild(v4); } TBoxView::~TBoxView() { CheckForFinalizeNoInit(&gMetaInfo); } struct RowColumnInfo { GCoordinate fPosition; GCoordinate fSize; bool fExpandable; }; void TBoxView::PlaceAndSizeChildren(const TGRect &r) { long expandableColumns= 0, expandableRows= 0; long columns, rows; long x, y; TView *v; TGArea a; GetColumnsRows(columns, rows); RowColumnInfo *xinfo = new(kSameHeap, this) RowColumnInfo[columns]; try { RowColumnInfo *yinfo = new(kSameHeap, this) RowColumnInfo[rows]; try { // first step: collect information about row/column and expandability for (x= 0; x < columns; x++) { bool expandable= false; if (fHExpand == kExpand) { for (y= 0; y < rows; y++) { if (GetCellHExpand(x, y) == kExpand) { expandable= true; break; } } if (expandable) expandableColumns++; } xinfo[x].fExpandable= expandable; xinfo[x].fSize= GetCellSize(x, 0).fX; } for (y= 0; y < rows; y++) { bool expandable= false; if (fVExpand == kExpand) { for (x= 0; x < columns; x++) { if (GetCellVExpand(x, y) == kExpand) { expandable= true; break; } } if (expandable) expandableRows++; } yinfo[y].fExpandable= expandable; yinfo[y].fSize= GetCellSize(0, y).fY; } // second step: calculate any additional space TGPoint actualSize(r.GetSize()); TGPoint naturalSize(GetWidth(), GetHeight()); TGPoint diffSize(actualSize-naturalSize); TGPoint delta(TGPoint::kOrigin), gapdelta(TGPoint::kOrigin); if (diffSize.fX > 0) { if (fHExpand == kExpand && expandableColumns > 0) delta.fX= diffSize.fX/expandableColumns; if (fHExpand == kGapExpand && columns > 1) gapdelta.fX= diffSize.fX/(columns-1); } if (diffSize.fY > 0) { if (fVExpand == kExpand && expandableRows > 0) delta.fY= diffSize.fY/expandableRows; if (fVExpand == kGapExpand && rows > 1) gapdelta.fY= diffSize.fY/(rows-1); } // third step: distribute any additional space // to gaps and/or expandable columns/rows if ( columns > 0 ) { xinfo[0].fPosition= 0; for (x= 0; x < columns; x++) { if (x > 0) xinfo[x].fPosition= xinfo[x-1].fPosition + xinfo[x-1].fSize + GetGapWidth(x-1) + gapdelta.fX; if (xinfo[x].fExpandable) xinfo[x].fSize+= delta.fX; } } if ( rows > 0 ) { yinfo[0].fPosition= 0; for (y= 0; y < rows; y++) { if (y > 0) yinfo[y].fPosition= yinfo[y-1].fPosition + yinfo[y-1].fSize + GetGapHeight(y-1) + gapdelta.fY; if (yinfo[y].fExpandable) yinfo[y].fSize+= delta.fY; } } // fourth step: resize subviews and/or align subviews in calculated cell bool wasVisible= GetVisibility(); SetVisibility(false); for (y= 0; y < rows; y++) { for (x= 0; x < columns; x++) { v= GetChildAt(x, y); if (v) { TGPoint cellpos(xinfo[x].fPosition, yinfo[y].fPosition); TGPoint cellsize(xinfo[x].fSize, yinfo[y].fSize); TGPoint newsize(GetItemSize(x, y)); if (fHExpand == kExpand && GetCellHExpand(x, y) == kExpand) newsize.fX= cellsize.fX; if (fVExpand == kExpand && GetCellVExpand(x, y) == kExpand) newsize.fY= cellsize.fY; AlignViewInCell(*v, GetCellHAlign(x, y), GetCellVAlign(x, y), cellpos, cellsize, newsize); } } } SetVisibility(wasVisible); } catch (...) { delete yinfo; throw; } delete yinfo; } catch (...) { delete xinfo; throw; } delete xinfo; } void TBoxView::AlignViewInCell(TView &v, EAlign ha, EAlign va, const TGPoint &cellpos, const TGPoint &cellsize, const TGPoint &size) const { TGPoint pos(cellpos); switch (ha) { case kLeft: break; case kCentered: pos.fX+= (cellsize.fX-size.fX)/2.0; break; case kRight: pos.fX+= cellsize.fX-size.fX; break; } switch (va) { case kTop: break; case kCentered: pos.fY+= (cellsize.fY-size.fY)/2.0; break; case kBottom: pos.fY+= cellsize.fY-size.fY; break; } v.SetAllocatedArea(TGRect(TGPoint::kOrigin, size)); v.TranslateAllocatedAreaInParentTo(pos); } void TBoxView::GetBounds(TGArea& area) const { area= TGRect(0, 0, GetWidth(), GetHeight()); } TBoxView::EAlign TBoxView::GetCellHAlign(long columns, long row) const { return fHAlign; } TBoxView::EAlign TBoxView::GetCellVAlign(long columns, long row) const { return fVAlign; } TBoxView::EExpand TBoxView::GetCellHExpand(long columns, long row) const { return fHExpand; } TBoxView::EExpand TBoxView::GetCellVExpand(long columns, long row) const { return fVExpand; } TGPoint TBoxView::GetCellPosition(long columns, long row) const { TGPoint o(0.0, 0.0); for (long x= 0; x < columns; x++) o.fX= o.fX + GetColumnWidth(x) + GetGapWidth(x); for (long y= 0; y < row; y++) o.fY= o.fY + GetRowHeight(y) + GetGapHeight(y); return o; } GCoordinate TBoxView::GetWidth() const { long columns, rows; GCoordinate width= 0.0; GetColumnsRows(columns, rows); for (long x= 0; x < columns; x++) { width+= GetColumnWidth(x); if (x < columns-1) width+= GetGapWidth(x); } return width; } GCoordinate TBoxView::GetHeight() const { long columns, rows; GCoordinate height= 0.0; GetColumnsRows(columns, rows); for (long y= 0; y < rows; y++) { height+= GetRowHeight(y); if (y < rows-1) height+= GetGapHeight(y); } return height; } void TBoxView::GetColumnsRows(long &columns, long &rows) const { long n= Count(); if (fCols <= 0) columns= n; else columns= fCols; if (fRows <= 0) rows= n; else rows= fRows; } GCoordinate TBoxView::GetGapWidth(long columns) const { return fGap.fX; } GCoordinate TBoxView::GetGapHeight(long row) const { return fGap.fY; } GCoordinate TBoxView::GetColumnWidth(long column) const { long columns, rows; GCoordinate width= 0.0; GetColumnsRows(columns, rows); for (long y= 0; y < rows; y++) width= Max(width, GetItemWidth(column, y)); return width; } GCoordinate TBoxView::GetRowHeight(long row) const { long columns, rows; GCoordinate height= 0.0; GetColumnsRows(columns, rows); for (long x= 0; x < columns; x++) height= Max(height, GetItemHeight(x, row)); return height; } TGPoint TBoxView::GetCellSize(long columns, long row) const { return TGPoint(GetColumnWidth(columns), GetRowHeight(row)); } GCoordinate TBoxView::GetItemWidth(long columns, long row) const { return GetItemSize(columns, row).fX; } GCoordinate TBoxView::GetItemHeight(long columns, long row) const { return GetItemSize(columns, row).fY; } TGPoint TBoxView::GetItemSize(long columns, long row) const { TView *v= GetChildAt(columns, row); if (v) { TGArea e; v->GetBounds(e); return e.GetBounds().GetSize(); } return TGPoint(0,0); } TView* TBoxView::GetChildAt(long column, long row) const { long columns, rows; GetColumnsRows(columns, rows); return GetChild(row*columns+column); } void TBoxView::SetColumns(const long &columns) { fCols = columns; } void TBoxView::SetRows(const long &rows) { fRows = rows; } void TBoxView::SetHAlign(EAlign ha) { fHAlign = ha; } void TBoxView::SetVAlign(EAlign va) { fVAlign = va; } void TBoxView::SetHExpand(EExpand he) { fHExpand = he; } void TBoxView::SetVExpand(EExpand va) { fVExpand = va; } void TBoxView::SetGapWidth(const GCoordinate &width) { fGap.fX = width; } void TBoxView::SetGapHeight(const GCoordinate &height) { fGap.fY = height; } // Miss Manners stuff MCollectibleDefinitionsMacro(TBoxView, 0); TBoxView::TBoxView() : VViewInitialize(&gMetaInfo), fCols(0), fRows(0), fGap(kDefaultGap, kDefaultGap), fHAlign(kLeft), fVAlign(kTop), fHExpand(kNoExpand), fVExpand(kNoExpand) { CheckForInitializeNoInit(&gMetaInfo); } TBoxView::TBoxView(const TBoxView &other) : VViewInitialize(&gMetaInfo), TLayoutView(other), fCols(other.fCols), fRows(other.fRows), fGap(other.fGap), fHAlign(other.fHAlign), fVAlign(other.fVAlign), fHExpand(other.fHExpand), fVExpand(other.fVExpand) { CheckForInitializeNoInit(&gMetaInfo); } TBoxView& TBoxView::operator=(const TBoxView& other) { if (&other != this) { TLayoutView::operator=(other); fCols= other.fCols; fRows= other.fRows; fGap= other.fGap; fHAlign= other.fHAlign; fVAlign= other.fVAlign; fHExpand= other.fHExpand; fVExpand= other.fVExpand; } return *this; } TStream& TBoxView::operator>>=(TStream& toWhere) const { WriteVersion(toWhere); TLayoutView::operator>>=(toWhere); fCols >>= toWhere; fRows >>= toWhere; fGap >>= toWhere; (unsigned long)fHAlign >>= toWhere; (unsigned long)fVAlign >>= toWhere; (unsigned long)fHExpand >>= toWhere; (unsigned long)fVExpand >>= toWhere; return toWhere; } TStream& TBoxView::operator<<=(TStream& fromWhere) { unsigned long tmp; switch (ReadVersion(fromWhere)) { case kOriginalVersion: TLayoutView::operator<<=(fromWhere); fCols <<= fromWhere; fRows <<= fromWhere; fGap <<= fromWhere; tmp <<= fromWhere; fHAlign= (EAlign) tmp; tmp <<= fromWhere; fVAlign= (EAlign) tmp; tmp <<= fromWhere; fHExpand= (EExpand) tmp; tmp <<= fromWhere; fVExpand= (EExpand) tmp; break; default: throw TInvalidVersionError(); } return fromWhere; } //---- THBoxView ------------------------------------------------------------------- THBoxView::THBoxView(TView* v1, TView* v2, TView* v3, TView* v4) : TBoxView(0, 1, v1, v2, v3, v4), VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } THBoxView::THBoxView(EAlign va, TView* v1, TView* v2, TView* v3, TView* v4) : TBoxView(0, 1, kLeft, va, v1, v2, v3, v4), VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } THBoxView::THBoxView(GCoordinate gap, TView* v1, TView* v2, TView* v3, TView* v4) : TBoxView(0, 1, TGPoint(gap,0), v1, v2, v3, v4), VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } THBoxView::~THBoxView() { CheckForFinalizeNoInit(&gMetaInfo); } // Miss Manners stuff MCollectibleDefinitionsMacro(THBoxView, 0); THBoxView::THBoxView() : VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } THBoxView::THBoxView(const THBoxView &other) : VViewInitialize(&gMetaInfo), TBoxView(other) { CheckForInitializeNoInit(&gMetaInfo); } THBoxView& THBoxView::operator=(const THBoxView& other) { if (&other != this) TBoxView::operator=(other); return *this; } //---- TVBoxView ------------------------------------------------------------------- TVBoxView::TVBoxView(TView* v1, TView* v2, TView* v3, TView* v4) : TBoxView(1, 0, v1, v2, v3, v4), VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } TVBoxView::TVBoxView(EAlign ha, TView* v1, TView* v2, TView* v3, TView* v4) : TBoxView(1, 0, ha, kTop, v1, v2, v3, v4), VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } TVBoxView::TVBoxView(GCoordinate gap, TView* v1, TView* v2, TView* v3, TView* v4) : TBoxView(1, 0, TGPoint(0,gap), v1, v2, v3, v4), VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } TVBoxView::~TVBoxView() { CheckForFinalizeNoInit(&gMetaInfo); } // Miss Manners stuff MCollectibleDefinitionsMacro(TVBoxView, 0); TVBoxView::TVBoxView() : VViewInitialize( &gMetaInfo ) { CheckForInitializeNoInit(&gMetaInfo); } TVBoxView::TVBoxView(const TVBoxView &other) : VViewInitialize(&gMetaInfo), TBoxView(other) { CheckForInitializeNoInit(&gMetaInfo); } TVBoxView& TVBoxView::operator=(const TVBoxView& other) { if (&other != this) TBoxView::operator=(other); return *this; } //---- TBackgroundView --------------------------------------------------------- TBackgroundView::TBackgroundView(const TGPoint &border, TGrafBundle *bundleToAdopt, TView* viewToAdopt) : fBorder(border), VViewInitialize(&gMetaInfo), TLayoutView() { if (bundleToAdopt) AdoptBundle(bundleToAdopt); if (viewToAdopt) AdoptChild(viewToAdopt); CheckForInitializeNoInit(&gMetaInfo); } TBackgroundView::TBackgroundView(const TGPoint &border, const TColor &color, TView* viewToAdopt) : fBorder(border), VViewInitialize(&gMetaInfo), TLayoutView() { AdoptBundle(new(kSameHeap, this) TGrafBundle(color, TGrafBundle::kFill)); if (viewToAdopt) AdoptChild(viewToAdopt); CheckForInitializeNoInit(&gMetaInfo); } TBackgroundView::TBackgroundView(const TGPoint &border, TView* viewToAdopt) : fBorder(border), VViewInitialize(&gMetaInfo), TLayoutView() { AdoptBundle(new(kSameHeap, this) TGrafBundle(TRGBColor(1.0,1.0,1.0), TGrafBundle::kFill)); if (viewToAdopt) AdoptChild(viewToAdopt); CheckForInitializeNoInit(&gMetaInfo); } TBackgroundView::~TBackgroundView() { CheckForFinalizeNoInit(&gMetaInfo); } void TBackgroundView::GetBounds(TGArea& area) const { TView *child= GetChild(0); if (child) { child->GetBounds(area); area= TGRect(TGPoint(0,0), area.GetBounds().GetSize()+2*fBorder); } else TLayoutView::GetBounds(area); } void TBackgroundView::DrawContents(TGrafPort& port) const { TGArea viewBounds; GetAllocatedArea(viewBounds); port.Draw(viewBounds, *GetBundle()); } void TBackgroundView::PlaceAndSizeChildren(const TGRect &r) { TView *child= GetChild(0); if (child) { child->SetAllocatedArea(TGRect(0, 0, r.GetSize().fX-2*fBorder.fX, r.GetSize().fY-2*fBorder.fY)); child->TranslateAllocatedAreaInParentTo(fBorder); } } // Miss Manners stuff MCollectibleDefinitionsMacro(TBackgroundView, 0); TBackgroundView::TBackgroundView() : fBorder(TGPoint(0,0)), VViewInitialize(&gMetaInfo) { CheckForInitializeNoInit(&gMetaInfo); } TBackgroundView::TBackgroundView(const TBackgroundView &other) : VViewInitialize(&gMetaInfo), fBorder(other.fBorder), TLayoutView(other) { CheckForInitializeNoInit(&gMetaInfo); } TBackgroundView& TBackgroundView::operator=(const TBackgroundView& other) { if (&other != this) { TLayoutView::operator=(other); fBorder= other.fBorder; } return *this; } TStream& TBackgroundView::operator>>=(TStream& toWhere) const { WriteVersion(toWhere); TLayoutView::operator>>=(toWhere); fBorder >>= toWhere; return toWhere; } TStream& TBackgroundView::operator<<=(TStream& fromWhere) { switch (ReadVersion(fromWhere)) { case kOriginalVersion: TLayoutView::operator<<=(fromWhere); fBorder <<= fromWhere; break; default: throw TInvalidVersionError(); } return fromWhere; }