<p>IfaSdrViewisgivenatconstructiontime,thecalleris responsiblefordestroyingthisobjectwhentheviewbecomes invalid(theviewscannotnotify).IfGetViewForwarder(sal_True) iscalled,theunderlyingshapeisputintoeditmode,theview forwarderreturnedencapsulatestheOutlinerViewandthenextcall toGetTextForwarder()yieldsaforwarderencapsulatingtheactual DrawOutliner.Thus,changesonthatOutlinerareimmediately reflectedonthescreen.Iftheobjectleaveseditmode,theold behaviourisrestored.</p>
*/ class SvxTextEditSourceImpl : public SfxListener, public SfxBroadcaster, public sdr::ObjectUser
{ private:
oslInterlockedCount maRefCount;
SdrObject* mpObject; // TTTT could be reference (?)
SdrText* mpText;
SdrView* mpView;
VclPtr<const OutputDevice> mpWindow;
SdrModel* mpModel; // TTTT probably not needed -> use SdrModel from SdrObject (?)
std::unique_ptr<SdrOutliner> mpOutliner;
std::unique_ptr<SvxOutlinerForwarder> mpTextForwarder;
std::unique_ptr<SvxDrawOutlinerViewForwarder> mpViewForwarder; // if non-NULL, use GetViewModeTextForwarder text forwarder
css::uno::Reference< css::linguistic2::XLinguServiceManager2 > m_xLinguServiceManager;
Point maTextOffset; bool mbDataValid; bool mbIsLocked; bool mbNeedsUpdate; bool mbOldUndoMode; bool mbForwarderIsEditMode; // have to reflect that, since ENDEDIT can happen more often bool mbShapeIsEditMode; // only true, if SdrHintKind::BeginEdit was received bool mbNotificationsDisabled; // prevent EditEngine/Outliner notifications (e.g. when setting up forwarder) bool mbNotifyEditOutlinerSet;
void SvxTextEditSourceImpl::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{ // #i105988 keep reference to this object
rtl::Reference< SvxTextEditSourceImpl > xThis( this );
if (SfxHintId::Dying == rHint.GetId())
{ if (&rBC == mpView)
{
mpView = nullptr;
mpViewForwarder.reset();
}
} elseif (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
{ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); switch( pSdrHint->GetKind() )
{ case SdrHintKind::ObjectChange:
{
mbDataValid = false; // Text has to be get again
if( HasView() )
{ // Update maTextOffset, object has changed // Cannot call that here, since TakeTextRect() (called from there) // changes outliner content. // UpdateOutliner();
// Broadcast object changes, as they might change visible attributes
SvxViewChangedHint aHint;
Broadcast( aHint );
} break;
}
case SdrHintKind::BeginEdit: if( mpObject == pSdrHint->GetObject() )
{ // Once SdrHintKind::BeginEdit is broadcast, each EditSource of // AccessibleCell will handle it here and call below: // mpView->GetTextEditOutliner()->SetNotifyHdl(), which // will replace the Notifier for current editable cell. It // is totally wrong. So add check here to avoid the // incorrect replacement of notifier.
// Currently it only happens on the editsource of // AccessibleCell if (mpObject && mpText)
{
sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mpObject ); if(pTableObj)
{ const sdr::table::CellRef& xCell = pTableObj->getActiveCell(); if (xCell.is())
{
sdr::table::Cell* pCellObj = dynamic_cast< sdr::table::Cell* >( mpText ); if (pCellObj && xCell.get() != pCellObj) break;
}
}
} // invalidate old forwarder if( !mbForwarderIsEditMode )
{
mpTextForwarder.reset();
}
// register as listener - need to broadcast state change messages if( mpView && mpView->GetTextEditOutliner() )
{
mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
mbNotifyEditOutlinerSet = true;
}
// Only now we're really in edit mode
mbShapeIsEditMode = true;
// destroy view forwarder, OutlinerView no longer // valid (no need for UpdateData(), it's been // synched on SdrEndTextEdit)
mpViewForwarder.reset();
// Invalidate text forwarder, we might // not be called again before entering edit mode a // second time! Then, the old outliner might be // invalid. if( mbForwarderIsEditMode )
{
mbForwarderIsEditMode = false;
mpTextForwarder.reset();
}
} break;
/* this is a callback from the attached SdrObject when it is actually deleted */ void SvxTextEditSourceImpl::ObjectInDestruction(const SdrObject&)
{
mpObject = nullptr;
dispose();
Broadcast( SfxHint( SfxHintId::Dying ) );
}
/* unregister at all objects and set all references to 0 */ void SvxTextEditSourceImpl::dispose()
{
mpTextForwarder.reset();
mpViewForwarder.reset();
void SvxTextEditSourceImpl::SetupOutliner()
{ // only for UAA edit source: setup outliner equivalently as in // SdrTextObj::Paint(), such that formatting equals screen // layout if( !(mpObject && mpOutliner) ) return;
// calc text offset from shape anchor
maTextOffset = aPaintRect.TopLeft() - aBoundRect.TopLeft();
}
}
void SvxTextEditSourceImpl::UpdateOutliner()
{ // only for UAA edit source: update outliner equivalently as in // SdrTextObj::Paint(), such that formatting equals screen // layout if( !(mpObject && mpOutliner) ) return;
std::optional<OutlinerParaObject> pOutlinerParaObject;
SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject ); if( pTextObj && pTextObj->getActiveText() == mpText )
pOutlinerParaObject = pTextObj->CreateEditOutlinerParaObject(); // Get the OutlinerParaObject if text edit is active bool bTextEditActive(false);
if( pOutlinerParaObject )
bTextEditActive = true; // text edit active elseif (mpText->GetOutlinerParaObject())
pOutlinerParaObject = *mpText->GetOutlinerParaObject();
// maybe we have to set the border attributes if (mpOutliner->GetParagraphCount()==1)
{ // if we only have one paragraph we check if it is empty
OUString aStr(mpOutliner->GetText(mpOutliner->GetParagraph(0)));
if (aStr.isEmpty())
{ // its empty, so we have to force the outliner to initialise itself
mpOutliner->SetText( u""_ustr, mpOutliner->GetParagraph( 0 ) );
auto pCell = dynamic_cast<sdr::table::Cell*>(mpText); if (pCell && pCell->GetStyleSheet())
mpOutliner->SetStyleSheet( 0, pCell->GetStyleSheet()); elseif (mpObject->GetStyleSheet())
mpOutliner->SetStyleSheet( 0, mpObject->GetStyleSheet());
}
}
mbDataValid = true;
}
if( bCreated && mpOutliner && HasView() )
{ // register as listener - need to broadcast state change messages // registration delayed until outliner is completely set up
mpOutliner->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
}
// prevent EE/Outliner notifications during setup
mbNotificationsDisabled = false;
// distinguish the cases // a) connected to view, maybe edit mode is active, can work directly on the EditOutliner // b) background Outliner, reflect changes into ParaOutlinerObject (this is exactly the old UNO code)
// IASS: testing for HasView() is *not* sufficient - there may be more views of one document // open and TextEdit is only active in one of them, or - as with IASS - it may even be the view // of the running SlideShow itself which also will have no active TextEdit and thus no Outliner. // Thus, to identify the view which indeed does have an outliner (and is in TextEdit mode), // also check if it has an active Outliner by using GetTextEditOutliner() if( HasView() && nullptr != mpView->GetTextEditOutliner() )
{ if( IsEditMode() != mbForwarderIsEditMode )
{ // forwarder mismatch - create new
mpTextForwarder.reset();
}
if( IsEditMode() ) return GetEditModeTextForwarder(); else return GetBackgroundTextForwarder();
} else
{ // tdf#123470 if the text edit mode of the shape is active, then we // cannot trust a previously cached TextForwarder state as the text may // be out of date, so force a refetch in that case, unless locked against // changes if (IsEditMode() && mpTextForwarder && !mbIsLocked)
{
assert(!mbForwarderIsEditMode); // because without a view there is no other option except !mbForwarderIsEditMode bool bTextEditActive = false;
SdrTextObj* pTextObj = DynCastSdrTextObj(mpObject); // similar to the GetBackgroundTextForwarder check, see if the text edit is active if (pTextObj && pTextObj->getActiveText() == mpText && pTextObj->CanCreateEditOutlinerParaObject())
bTextEditActive = true; // text edit active if (bTextEditActive)
mbDataValid = false;
}
return GetBackgroundTextForwarder();
}
}
std::unique_ptr<SvxDrawOutlinerViewForwarder> SvxTextEditSourceImpl::CreateViewForwarder()
{ if( mpView->GetTextEditOutlinerView() && mpObject )
{ // register as listener - need to broadcast state change messages
mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
mbNotifyEditOutlinerSet = true;
// shall we delete? if( mpViewForwarder )
{ if( !IsEditMode() )
{ // destroy all forwarders (no need for UpdateData(), // it's been synched on SdrEndTextEdit)
mpViewForwarder.reset();
}
} // which to create? Directly in edit mode, create new, or none? elseif( mpView )
{ if( IsEditMode() )
{ // create new view forwarder
mpViewForwarder = CreateViewForwarder();
} elseif( bCreate )
{ // dispose old text forwarder
UpdateData();
mpTextForwarder.reset();
// enter edit mode
mpView->SdrEndTextEdit();
if(mpView->SdrBeginTextEdit(mpObject))
{
SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject ); if (pTextObj && pTextObj->IsTextEditActive())
{ // create new view forwarder
mpViewForwarder = CreateViewForwarder();
} else
{ // failure. Somehow, SdrBeginTextEdit did not set // our SdrTextObj into edit mode
mpView->SdrEndTextEdit();
}
}
}
}
return mpViewForwarder.get();
}
void SvxTextEditSourceImpl::UpdateData()
{ // if we have a view and in edit mode, we're working with the // DrawOutliner. Thus, all changes made on the text forwarder are // reflected on the view and committed to the model on // SdrEndTextEdit(). Thus, no need for explicit updates here. if( HasView() && IsEditMode() ) return;
void SvxTextEditSourceImpl::lock()
{ // if this assert ever fires, we will need to make this a counter instead of a boolean
assert(!mbIsLocked && "cannot nest these loc() calls");
mbIsLocked = true; if( mpOutliner )
{ const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->SetUpdateLayout( false );
mbOldUndoMode = mpOutliner->GetEditEngine().IsUndoEnabled(); const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->EnableUndo( false );
}
}
Point SvxTextEditSourceImpl::LogicToPixel( const Point& rPoint, const MapMode& rMapMode )
{ // The responsibilities of ViewForwarder happen to be // somewhat mixed in this case. On the one hand, we need the // different interface queries on the SvxEditSource interface, // since we need both VisAreas. On the other hand, if an // EditViewForwarder exists, maTextOffset does not remain static, // but may change with every key press. if( IsEditMode() )
{
SvxEditViewForwarder* pForwarder = GetEditViewForwarder(false);
Point SvxTextEditSourceImpl::PixelToLogic( const Point& rPoint, const MapMode& rMapMode )
{ // The responsibilities of ViewForwarder happen to be // somewhat mixed in this case. On the one hand, we need the // different interface queries on the SvxEditSource interface, // since we need both VisAreas. On the other hand, if an // EditViewForwarder exists, maTextOffset does not remain static, // but may change with every key press. if( IsEditMode() )
{
SvxEditViewForwarder* pForwarder = GetEditViewForwarder(false);
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.