/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
namespace
{ /// Gets the bottom position which is a deadline for a split fly.
SwTwips GetFlyAnchorBottom(SwFlyFrame* pFly, const SwFrame& rAnchor)
{
SwRectFnSet aRectFnSet(pFly);
constauto* pFrameFormat = pFly->GetFrameFormat(); const IDocumentSettingAccess& rIDSA = pFrameFormat->getIDocumentSettingAccess(); // Allow overlap with bottom margin / footer only in case we're relative to the page frame. bool bVertPageFrame = pFrameFormat->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME; bool bInBody = rAnchor.IsInDocBody(); bool bLegacy = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN) && (bVertPageFrame || !bInBody); if (bLegacy)
{ // Word <= 2010 style: the fly can overlap with the bottom margin / footer area in case the // fly height fits the body height and the fly bottom fits the page. // See if the fly height would fit at least the page height, ignoring the vertical offset.
SwTwips nFlyHeight = aRectFnSet.GetHeight(pFly->getFrameArea());
SwTwips nPageHeight = aRectFnSet.GetHeight(pPage->getFramePrintArea());
SwTwips nFlyTop = aRectFnSet.GetTop(pFly->getFrameArea());
SwTwips nBodyTop = aRectFnSet.GetTop(pBody->getFrameArea()); if (nFlyTop < nBodyTop)
{ // Fly frame overlaps with the top margin area, ignore that part of the fly frame for // top/height purposes.
nFlyHeight -= nBodyTop - nFlyTop;
nFlyTop = nBodyTop;
} if (nFlyHeight <= nPageHeight)
{ // Yes, it would fit: allow overlap if there is no problematic vertical offset.
SwTwips nDeadline = aRectFnSet.GetBottom(pPage->getFrameArea());
SwTwips nBodyHeight = aRectFnSet.GetHeight(pBody->getFramePrintArea()); if (nDeadline - nFlyTop > nBodyHeight)
{ // If the fly would now grow to nDeadline then it would not fit the body height, so // limit the height.
nDeadline = nFlyTop + nBodyHeight;
} return nDeadline;
}
}
// Word >= 2013 style: the fly has to stay inside the body frame. return aRectFnSet.GetPrtBottom(*pBody);
}
}
// First the Init, then the Content: // This is due to the fact that the Content may have Objects/Frames, // which are then registered
InitDrawObj(*pAnch);
Chain( pAnch );
if (!bFollow)
{
InsertCnt();
}
// Put it somewhere outside so that out document is not formatted unnecessarily often
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Pos().setX(FAR_AWAY);
aFrm.Pos().setY(FAR_AWAY);
}
void SwFlyFrame::Chain( SwFrame* _pAnch )
{ // Connect to chain neighbours. // No problem, if a neighbor doesn't exist - the construction of the // neighbor will make the connection const SwFormatChain& rChain = GetFormat()->GetChain(); if ( !(rChain.GetPrev() || rChain.GetNext()) ) return;
void SwFlyFrame::InsertCnt()
{ if ( GetPrevLink() ) return;
const SwFormatContent& rContent = GetFormat()->GetContent();
OSL_ENSURE( rContent.GetContentIdx(), ":-( no content prepared." );
SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex(); // Lower() means SwColumnFrame; the Content then needs to be inserted into the (Column)BodyFrame
::InsertCnt_( Lower() ? static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(Lower())->Lower()) : static_cast<SwLayoutFrame*>(this),
GetFormat()->GetDoc(), nIndex );
// NoText always have a fixed height.
SwFrame* pLower = Lower(); if ( pLower && pLower->IsNoTextFrame() )
{
mbFixSize = true;
m_bMinHeight = false;
}
}
void SwFlyFrame::InsertColumns()
{ // #i97379# // Check, if column are allowed. // Columns are not allowed for fly frames, which represent graphics or embedded objects. const SwFormatContent& rContent = GetFormat()->GetContent();
OSL_ENSURE( rContent.GetContentIdx(), " - no content prepared." );
SwNodeIndex aFirstContent( *(rContent.GetContentIdx()), 1 ); if ( aFirstContent.GetNode().IsNoTextNode() )
{ return;
}
// Start off PrtArea to be as large as Frame, so that we can put in the columns // properly. It'll adjust later on.
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
aPrt.Width( getFrameArea().Width() );
aPrt.Height( getFrameArea().Height() );
}
const SwFormatCol aOld; // ChgColumns() also needs an old value passed
ChgColumns( aOld, rCol );
}
void SwFlyFrame::DestroyImpl()
{ // Accessible objects for fly frames will be destroyed in this destructor. // For frames bound as char or frames that don't have an anchor we have // to do that ourselves. For any other frame the call RemoveFly at the // anchor will do that. #if !ENABLE_WASM_STRIP_ACCESSIBILITY if( IsAccessibleFrame() && GetFormat() && (IsFlyInContentFrame() || !GetAnchorFrame()) )
{
SwRootFrame *pRootFrame = getRootFrame(); if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
{
SwViewShell *pVSh = pRootFrame->GetCurrShell(); if( pVSh && pVSh->Imp() )
{ // Lowers aren't disposed already, so we have to do a recursive // dispose
pVSh->Imp()->DisposeAccessibleFrame( this, true );
}
}
} #endif
if( GetFormat() && !GetFormat()->GetDoc().IsInDtor() )
{
ClearTmpConsiderWrapInfluence(); // remove this from SwLayouter
Unchain();
DeleteCnt();
if ( GetAnchorFrame() )
AnchorFrame()->RemoveFly( this );
}
void SwFlyFrame::FinitDrawObj()
{ if(!GetVirtDrawObj() ) return;
SwFormat* pFormat = GetFormat(); // Deregister from SdrPageViews if the Objects is still selected there. if(!pFormat->GetDoc().IsInDtor())
{
SwViewShell* p1St = getRootFrame()->GetCurrShell(); if(p1St)
{ for(SwViewShell& rCurrentShell : p1St->GetRingContainer())
{ // At the moment the Drawing can do just do an Unmark on everything, // as the Object was already removed if (rCurrentShell.HasDrawView() &&
rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount())
{
SwFlyFrame const*const pOldSelFly = ::GetFlyFromMarked(nullptr, &rCurrentShell); if (pOldSelFly == this)
{
assert(rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1); if (SwFEShell *const pFEShell = dynamic_cast<SwFEShell*>(&rCurrentShell))
{ // tdf#131679 move any cursor out of fly
rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
SwPaM const temp(ResolveFlyAnchor(*pOldSelFly->GetFormat()));
pFEShell->SetSelection(temp); // could also call SetCursor() like SwFEShell::SelectObj() // does, but that would access layout a bit much...
} else
{
rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
}
}
}
}
}
}
SwVirtFlyDrawObj* pVirtDrawObj = GetVirtDrawObj(); // Else calls delete of the ContactObj
pVirtDrawObj->SetUserCall(nullptr);
if ( pVirtDrawObj->getSdrPageFromSdrObject() )
pVirtDrawObj->getSdrPageFromSdrObject()->RemoveObject( pVirtDrawObj->GetOrdNum() );
ClearDrawObj();
}
void SwFlyFrame::ChainFrames( SwFlyFrame &rMaster, SwFlyFrame &rFollow )
{
OSL_ENSURE( !rMaster.GetNextLink(), "link can not be changed" );
OSL_ENSURE( !rFollow.GetPrevLink(), "link can not be changed" );
if ( rMaster.ContainsContent() )
{ // To get a text flow we need to invalidate
SwFrame *pInva = rMaster.FindLastLower();
SwRectFnSet aRectFnSet(&rMaster); const tools::Long nBottom = aRectFnSet.GetPrtBottom(rMaster); while ( pInva )
{ if( aRectFnSet.BottomDist( pInva->getFrameArea(), nBottom ) <= 0 )
{
pInva->InvalidateSize();
pInva->Prepare();
pInva = pInva->FindPrev();
} else
pInva = nullptr;
}
}
if ( rFollow.ContainsContent() )
{ // There's only the content from the Masters left; the content from the Follow // does not have any Frames left (should always be exactly one empty TextNode).
SwFrame *pFrame = rFollow.ContainsContent();
OSL_ENSURE( !pFrame->IsTabFrame() && !pFrame->FindNext(), "follow in chain contains content" );
pFrame->Cut();
SwFrame::DestroyFrame(pFrame);
}
if ( rFollow.ContainsContent() )
{ // The Master sucks up the content of the Follow
SwLayoutFrame *pUpper = &rMaster;
SwFrame* pLower = pUpper->Lower(); if ( pLower && pLower->IsColumnFrame() )
{
pUpper = static_cast<SwLayoutFrame*>(pUpper->GetLastLower());
pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower()); // The (Column)BodyFrame
OSL_ENSURE( pUpper && pUpper->IsColBodyFrame(), "Missing ColumnBody" );
}
SwFlyFrame *pFoll = &rFollow; while ( pFoll )
{
SwFrame *pTmp = ::SaveContent( pFoll ); if ( pTmp )
::RestoreContent( pTmp, pUpper, rMaster.FindLastLower() );
pFoll->SetCompletePaint();
pFoll->InvalidateSize();
pFoll = pFoll->GetNextLink();
}
}
// The Follow needs his own content to be served const SwFormatContent &rContent = rFollow.GetFormat()->GetContent();
OSL_ENSURE( rContent.GetContentIdx(), ":-( No content prepared." );
SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex(); // Lower() means SwColumnFrame: this one contains another SwBodyFrame
SwFrame* pLower = rFollow.Lower();
::InsertCnt_( pLower ? const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pLower)->Lower()))
: static_cast<SwLayoutFrame*>(&rFollow),
rFollow.GetFormat()->GetDoc(), ++nIndex );
SwFlyFrame *SwFlyFrame::FindChainNeighbour( SwFrameFormat const &rChain, SwFrame *pAnch )
{ // We look for the Fly that's in the same Area. // Areas can for now only be Head/Footer or Flys.
if ( !pAnch ) // If an Anchor was passed along, that one counts (ctor!)
pAnch = AnchorFrame();
SwLayoutFrame *pLay; if ( pAnch->IsInFly() )
pLay = pAnch->FindFlyFrame(); else
{ // FindFooterOrHeader is not appropriate here, as we may not have a // connection to the Anchor yet.
pLay = pAnch->GetUpper(); while ( pLay && !(pLay->GetType() & (SwFrameType::Header|SwFrameType::Footer)) )
pLay = pLay->GetUpper();
}
SwIterator<SwFlyFrame,SwFormat> aIter( rChain );
SwFlyFrame *pFly = aIter.First(); if ( pLay )
{ while ( pFly )
{ if ( pFly->GetAnchorFrame() )
{ if ( pFly->GetAnchorFrame()->IsInFly() )
{ if ( pFly->AnchorFrame()->FindFlyFrame() == pLay ) break;
} elseif ( pLay == pFly->FindFooterOrHeader() ) break;
}
pFly = aIter.Next();
}
} elseif ( pFly )
{
OSL_ENSURE( !aIter.Next(), "chain with more than one instance" );
} return pFly;
}
bool SwFlyFrame::IsFlySplitAllowed() const
{ if (!IsFlyAtContentFrame())
{ returnfalse;
}
const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess(); if (rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES))
{ returnfalse;
}
if (FindFooterOrHeader())
{ // Adding a new page would not increase the header/footer area. returnfalse;
}
const SwFrame* pFlyAnchor = GetAnchorFrame(); if (pFlyAnchor && pFlyAnchor->FindColFrame())
{ // No split in multi-column sections, so GetFlyAnchorBottom() can assume that our innermost // body frame and the page's body frame is the same. // This is also consistent with the Word behavior. returnfalse;
}
if (pFlyAnchor && pFlyAnchor->IsInFootnote())
{ // No split in footnotes. returnfalse;
}
const SwFlyFrameFormat* pFormat = GetFormat(); const SwFormatVertOrient& rVertOrient = pFormat->GetVertOrient(); if (rVertOrient.GetVertOrient() == text::VertOrientation::BOTTOM)
{ // We have to grow from bottom to top, and the fly split code assumes that we grow from top // to bottom, so don't split for now. if (rVertOrient.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA)
{ // Growing from the bottom of the body frame. returnfalse;
}
}
return pFormat->GetFlySplit().GetValue();
}
SwFrame *SwFlyFrame::FindLastLower()
{
SwFrame *pRet = ContainsAny(); if ( pRet && pRet->IsInTab() )
pRet = pRet->FindTabFrame();
SwFrame *pNxt = pRet; while ( pNxt && IsAnLower( pNxt ) )
{ pRet = pNxt;
pNxt = pNxt->FindNext();
} return pRet;
}
bool SwFlyFrame::FrameSizeChg( const SwFormatFrameSize &rFrameSize )
{ bool bRet = false;
SwTwips nDiffHeight = getFrameArea().Height(); if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable )
mbFixSize = m_bMinHeight = false; else
{ if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
{
mbFixSize = true;
m_bMinHeight = false;
} elseif ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
{
mbFixSize = false;
m_bMinHeight = true;
}
nDiffHeight -= rFrameSize.GetHeight();
} // If the Fly contains columns, we already need to set the Fly // and the Columns to the required value or else we run into problems. if (SwFrame* pLower = Lower())
{ if ( pLower->IsColumnFrame() )
{ const SwRect aOld( GetObjRectWithSpaces() ); const Size aOldSz( getFramePrintArea().SSize() ); const SwTwips nDiffWidth = getFrameArea().Width() - rFrameSize.GetWidth();
// #i87645# - reset flags for the layout process (only if something has been invalidated)
ResetLayoutProcessBools();
} elseif (rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
{ // There's a FlyFrame, so use it static_cast<const sw::AutoFormatUsedHint&>(rHint).SetUsed(); return;
} elseif (rHint.GetId() == SfxHintId::SwGetZOrder)
{ auto pGetZOrdnerHint = static_cast<const sw::GetZOrderHint*>(&rHint); constauto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod)); if (rFormat.Which() == RES_FLYFRMFMT && rFormat.getIDocumentLayoutAccess().GetCurrentViewShell()) // #i11176#
pGetZOrdnerHint->m_rnZOrder = GetVirtDrawObj()->GetOrdNum();
} elseif (rHint.GetId() == SfxHintId::SwGetObjectConnected)
{ auto pConnectedHint = static_cast<const sw::GetObjectConnectedHint*>(&rHint); constauto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod)); if (!pConnectedHint->m_risConnected && rFormat.Which() == RES_FLYFRMFMT && (!pConnectedHint->m_pRoot || pConnectedHint->m_pRoot == getRootFrame()))
pConnectedHint->m_risConnected = true;
}
}
void SwFlyFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
SwFlyFrameInvFlags &rInvFlags,
SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
{ bool bClear = true; const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
SwViewShell *pSh = getRootFrame()->GetCurrShell(); switch( nWhich )
{ case RES_VERT_ORIENT: case RES_HORI_ORIENT: // #i18732# - consider new option 'follow text flow' case RES_FOLLOW_TEXT_FLOW:
{ // ATTENTION: Always also change Action in ChgRePos()!
rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
} break; // #i28701# - consider new option 'wrap influence on position' case RES_WRAP_INFLUENCE_ON_OBJPOS:
{
rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack
| SwFlyFrameInvFlags::UpdateObjInSortedList;
} break; case RES_SURROUND:
{ //#i28701# - invalidate position on change of // wrapping style.
rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache; // The background needs to be messaged and invalidated const SwRect aTmp( GetObjRectWithSpaces() );
NotifyBackground( FindPageFrame(), aTmp, PrepareHint::FlyFrameAttributesChanged );
// By changing the flow of frame-bound Frames, a vertical alignment // can be activated/deactivated => MakeFlyPos if( RndStdIds::FLY_AT_FLY == GetFormat()->GetAnchor().GetAnchorId() )
rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
// Delete contour in the Node if necessary
SwFrame* pLower = Lower(); if ( pLower && pLower->IsNoTextFrame() &&
!GetFormat()->GetSurround().IsContour() )
{
SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(pLower)->GetNode()); if ( pNd->HasContour() )
pNd->SetContour( nullptr );
} // #i28701# - perform reorder of object lists // at anchor frame and at page frame.
rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
} break;
SwFormatFrameSize *pNewFormatFrameSize = nullptr; if (nWhich == RES_FRM_SIZE)
pNewFormatFrameSize = const_cast<SwFormatFrameSize*>(static_cast<const SwFormatFrameSize*>(pNew)); elseif (nWhich == RES_FLY_SPLIT)
{ // If the fly frame has a table lower, invalidate that, so it joins its follow tab // frames and re-splits according to the new fly split rule. if (Lower() && Lower()->IsTabFrame())
{
Lower()->InvalidateAll_();
}
}
if (aURL.GetMap() && pNewFormatFrameSize)
{ const SwFormatFrameSize &rOld = *pNewFormatFrameSize; //#35091# Can be "times zero", when loading the template if ( rOld.GetWidth() && rOld.GetHeight() )
{
// Special case: // When assigning a template we cannot rely on the old column // attribute. As there need to be at least enough for ChgColumns, // we need to create a temporary attribute.
SwFormatCol aCol; if ( Lower() && Lower()->IsColumnFrame() )
{
sal_uInt16 nCol = 0;
SwFrame *pTmp = Lower(); do
{ ++nCol;
pTmp = pTmp->GetNext();
} while ( pTmp );
aCol.Init( nCol, 0, 1000 );
}
ChgColumns( aCol, GetFormat()->GetCol() );
}
SwFormatURL aURL( GetFormat()->GetURL() );
if (aURL.GetMap() && pOldFormat)
{ const SwFormatFrameSize &rOld = pOldFormat->GetFrameSize(); //#35091# Can be "times zero", when loading the template if ( rOld.GetWidth() && rOld.GetHeight() )
{
SwFlyFrame *pFrame; if ( GetAnchorFrame() && nullptr != (pFrame = AnchorFrame()->FindFlyFrame()) )
{ // Very bad case: If the Fly is bound within another Fly which // contains columns, the Format should be from that one.
SwFrame* pLower = pFrame->Lower(); if ( !pFrame->IsLocked() && !pFrame->IsColLocked() &&
pLower && pLower->IsColumnFrame() )
pFrame->InvalidateSize();
}
// #i85216# // if vertical position is oriented at a layout frame inside a ghost section, // assure that the position is invalidated and that the information about // the vertical position oriented frame is cleared if ( GetVertPosOrientFrame() && GetVertPosOrientFrame()->IsLayoutFrame() )
{ const SwSectionFrame* pSectFrame( GetVertPosOrientFrame()->FindSctFrame() ); if ( pSectFrame && pSectFrame->GetSection() == nullptr )
{
InvalidatePos();
ClearVertPosOrientFrame();
}
}
}
/** Change the relative position * * The position will be Fix automatically and the attribute is changed accordingly.
*/ void SwFlyFrame::ChgRelPos( const Point &rNewPos )
{ if ( GetCurrRelPos() == rNewPos ) return;
SwTwips nRemaining = CalcContentHeight(pAttrs, nMinHeight, nUL); if( IsMinHeight() && (nRemaining + nUL) < nMinHeight )
nRemaining = nMinHeight - nUL; // Because the Grow/Shrink of the Flys does not directly // set the size - only indirectly by triggering a Format() // via Invalidate() - the sizes need to be set here. // Notification is running along already. // As we already got a lot of zeros per attribute, we block them // from now on.
if ( nRemaining < MINFLY )
nRemaining = MINFLY;
const SwFrame* pAnchor = GetAnchorFrame(); if (SwFrame* pAnchorChar = FindAnchorCharFrame())
{ // If we find a follow of the anchor that is effectively the anchor of this fly, // then use that as the anchor for sizing purposes.
pAnchor = pAnchorChar;
} if (pAnchor && IsFlySplitAllowed())
{ // If the fly is allowed to be split, then limit its size to the upper of the // anchor.
SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
SwTwips nBottom = aRectFnSet.GetTop(getFrameArea()) + nRemaining; if (nBottom > nDeadline)
{ if (nDeadline > nTop)
{
nRemaining = nDeadline - nTop;
} else
{ // Even the top is below the deadline, set size to empty and mark it as // clipped so we re-format later.
nRemaining = 0;
m_bHeightClipped = true;
}
}
}
if (SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT))
{ // This fly is a textbox of a draw shape.
SdrObject* pShape = pShapeFormat->FindSdrObject(); if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>( pShape) )
{ // The shape is a customshape: then inform it about the calculated fly size.
Size aSize(getFrameArea().Width(), getFrameArea().Height());
pCustomShape->SuggestTextFrameSize(aSize); // Do the calculations normally done after touching editeng text of the shape.
pCustomShape->NbcSetOutlinerParaObjectForText(std::nullopt, nullptr);
}
}
} else
{ // Fixed Frames do not Format itself
setFrameAreaSizeValid(true);
// Flys set their size using the attr
SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
nNewSize -= nUL; if( nNewSize < MINFLY )
nNewSize = MINFLY;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.