blob: 034694c54e0935a729dca50b8bff51d66170531f [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"
#include <com/sun/star/uno/XInterface.hpp>
#include <vcl/svapp.hxx>
#include <svx/unoshtxt.hxx>
#include <editeng/unoedhlp.hxx>
#include <svl/lstner.hxx>
#include <rtl/ref.hxx>
#include <osl/mutex.hxx>
#include <svl/hint.hxx>
#include <svl/style.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdoutl.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdview.hxx>
#include <svx/svdetc.hxx>
#include <editeng/outliner.hxx>
#include <editeng/unoforou.hxx>
#include <editeng/unoviwou.hxx>
#include <editeng/outlobj.hxx>
#include <svx/svdotext.hxx>
#include <svx/svdpage.hxx>
#include <editeng/editeng.hxx>
#include <editeng/editobj.hxx>
#include <editeng/unotext.hxx>
#include <com/sun/star/linguistic2/XLinguServiceManager.hpp>
#include <comphelper/processfactory.hxx>
#include <vos/mutex.hxx>
#include <svx/svdotable.hxx>
#include <../table/cell.hxx>
#include <svx/sdrpaintwindow.hxx>
using namespace ::osl;
using namespace ::vos;
using namespace ::rtl;
using ::com::sun::star::uno::XInterface;
namespace css = ::com::sun::star;
//------------------------------------------------------------------------
// SvxTextEditSourceImpl
//------------------------------------------------------------------------
/** @descr
<p>This class essentially provides the text and view forwarders. If
no SdrView is given, this class handles the UNO objects, which are
currently not concerned with view issues. In this case,
GetViewForwarder() always returns NULL and the underlying
EditEngine of the SvxTextForwarder is a background one (i.e. not
the official DrawOutliner, but one created exclusively for this
object, with no relation to a view).
</p>
<p>If a SdrView is given at construction time, the caller is
responsible for destroying this object when the view becomes
invalid (the views cannot notify). If GetViewForwarder(sal_True)
is called, the underlying shape is put into edit mode, the view
forwarder returned encapsulates the OutlinerView and the next call
to GetTextForwarder() yields a forwarder encapsulating the actual
DrawOutliner. Thus, changes on that Outliner are immediately
reflected on the screen. If the object leaves edit mode, the old
behaviour is restored.</p>
*/
class SvxTextEditSourceImpl : public SfxListener, public SfxBroadcaster, public sdr::ObjectUser
{
private:
oslInterlockedCount maRefCount;
SdrObject* mpObject;
SdrText* mpText;
SdrView* mpView;
const Window* mpWindow;
SdrModel* mpModel;
SdrOutliner* mpOutliner;
SvxOutlinerForwarder* mpTextForwarder;
SvxDrawOutlinerViewForwarder* mpViewForwarder; // if non-NULL, use GetViewModeTextForwarder text forwarder
css::uno::Reference< css::linguistic2::XLinguServiceManager > m_xLinguServiceManager;
Point maTextOffset;
sal_Bool mbDataValid;
sal_Bool mbDestroyed;
sal_Bool mbIsLocked;
sal_Bool mbNeedsUpdate;
sal_Bool mbOldUndoMode;
sal_Bool mbForwarderIsEditMode; // have to reflect that, since ENDEDIT can happen more often
sal_Bool mbShapeIsEditMode; // #104157# only true, if HINT_BEGEDIT was received
sal_Bool mbNotificationsDisabled; // prevent EditEngine/Outliner notifications (e.g. when setting up forwarder)
XInterface* mpOwner;
SvxUnoTextRangeBaseList maTextRanges;
SvxTextForwarder* GetBackgroundTextForwarder();
SvxTextForwarder* GetEditModeTextForwarder();
SvxDrawOutlinerViewForwarder* CreateViewForwarder();
void SetupOutliner();
sal_Bool HasView() const { return mpView ? sal_True : sal_False; }
sal_Bool IsEditMode() const
{
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
return mbShapeIsEditMode && pTextObj && pTextObj->IsTextEditActive() ? sal_True : sal_False;
}
void dispose();
public:
SvxTextEditSourceImpl( SdrObject* pObject, SdrText* pText, XInterface* pOwner );
SvxTextEditSourceImpl( SdrObject& rObject, SdrText* pText, SdrView& rView, const Window& rWindow );
~SvxTextEditSourceImpl();
void SAL_CALL acquire();
void SAL_CALL release();
virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint );
SvxEditSource* Clone() const;
SvxTextForwarder* GetTextForwarder();
SvxEditViewForwarder* GetEditViewForwarder( sal_Bool );
void UpdateData();
void addRange( SvxUnoTextRangeBase* pNewRange );
void removeRange( SvxUnoTextRangeBase* pOldRange );
const SvxUnoTextRangeBaseList& getRanges() const;
SdrObject* GetSdrObject() const { return mpObject; }
void lock();
void unlock();
sal_Bool IsValid() const;
Rectangle GetVisArea();
Point LogicToPixel( const Point&, const MapMode& rMapMode );
Point PixelToLogic( const Point&, const MapMode& rMapMode );
DECL_LINK( NotifyHdl, EENotify* );
virtual void ObjectInDestruction(const SdrObject& rObject);
void ChangeModel( SdrModel* pNewModel );
void UpdateOutliner();
};
//------------------------------------------------------------------------
SvxTextEditSourceImpl::SvxTextEditSourceImpl( SdrObject* pObject, SdrText* pText, XInterface* pOwner )
: maRefCount ( 0 ),
mpObject ( pObject ),
mpText ( pText ),
mpView ( NULL ),
mpWindow ( NULL ),
mpModel ( pObject ? pObject->GetModel() : NULL ),
mpOutliner ( NULL ),
mpTextForwarder ( NULL ),
mpViewForwarder ( NULL ),
mbDataValid ( sal_False ),
mbDestroyed ( sal_False ),
mbIsLocked ( sal_False ),
mbNeedsUpdate ( sal_False ),
mbOldUndoMode ( sal_False ),
mbForwarderIsEditMode ( sal_False ),
mbShapeIsEditMode ( sal_False ),
mbNotificationsDisabled ( sal_False ),
mpOwner( pOwner )
{
DBG_ASSERT( mpObject, "invalid pObject!" );
if( !mpText )
{
SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mpObject );
if( pTextObj )
mpText = pTextObj->getText( 0 );
}
if( mpModel )
StartListening( *mpModel );
if( mpObject )
mpObject->AddObjectUser( *this );
}
//------------------------------------------------------------------------
SvxTextEditSourceImpl::SvxTextEditSourceImpl( SdrObject& rObject, SdrText* pText, SdrView& rView, const Window& rWindow )
: maRefCount ( 0 ),
mpObject ( &rObject ),
mpText ( pText ),
mpView ( &rView ),
mpWindow ( &rWindow ),
mpModel ( rObject.GetModel() ),
mpOutliner ( NULL ),
mpTextForwarder ( NULL ),
mpViewForwarder ( NULL ),
mbDataValid ( sal_False ),
mbDestroyed ( sal_False ),
mbIsLocked ( sal_False ),
mbNeedsUpdate ( sal_False ),
mbOldUndoMode ( sal_False ),
mbForwarderIsEditMode ( sal_False ),
mbShapeIsEditMode ( sal_True ),
mbNotificationsDisabled ( sal_False ),
mpOwner(0)
{
if( !mpText )
{
SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mpObject );
if( pTextObj )
mpText = pTextObj->getText( 0 );
}
if( mpModel )
StartListening( *mpModel );
if( mpView )
StartListening( *mpView );
if( mpObject )
mpObject->AddObjectUser( *this );
// #104157# Init edit mode state from shape info (IsTextEditActive())
mbShapeIsEditMode = IsEditMode();
}
//------------------------------------------------------------------------
SvxTextEditSourceImpl::~SvxTextEditSourceImpl()
{
DBG_ASSERT( mbIsLocked == sal_False, "text edit source was not unlocked before dispose!" );
if( mpObject )
mpObject->RemoveObjectUser( *this );
dispose();
}
//------------------------------------------------------------------------
void SvxTextEditSourceImpl::addRange( SvxUnoTextRangeBase* pNewRange )
{
if( pNewRange )
if( std::find( maTextRanges.begin(), maTextRanges.end(), pNewRange ) == maTextRanges.end() )
maTextRanges.push_back( pNewRange );
}
//------------------------------------------------------------------------
void SvxTextEditSourceImpl::removeRange( SvxUnoTextRangeBase* pOldRange )
{
if( pOldRange )
maTextRanges.remove( pOldRange );
}
//------------------------------------------------------------------------
const SvxUnoTextRangeBaseList& SvxTextEditSourceImpl::getRanges() const
{
return maTextRanges;
}
//------------------------------------------------------------------------
void SAL_CALL SvxTextEditSourceImpl::acquire()
{
osl_incrementInterlockedCount( &maRefCount );
}
//------------------------------------------------------------------------
void SAL_CALL SvxTextEditSourceImpl::release()
{
if( ! osl_decrementInterlockedCount( &maRefCount ) )
delete this;
}
void SvxTextEditSourceImpl::ChangeModel( SdrModel* pNewModel )
{
if( mpModel != pNewModel )
{
if( mpModel )
EndListening( *mpModel );
if( mpOutliner )
{
if( mpModel )
mpModel->disposeOutliner( mpOutliner );
else
delete mpOutliner;
mpOutliner = 0;
}
if( mpView )
{
EndListening( *mpView );
mpView = 0;
}
mpWindow = 0;
m_xLinguServiceManager.clear();
mpOwner = 0;
mpModel = pNewModel;
if( mpTextForwarder )
{
delete mpTextForwarder;
mpTextForwarder = 0;
}
if( mpViewForwarder )
{
delete mpViewForwarder;
mpViewForwarder = 0;
}
if( mpModel )
StartListening( *mpModel );
}
}
//------------------------------------------------------------------------
void SvxTextEditSourceImpl::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
// #i105988 keep reference to this object
rtl::Reference< SvxTextEditSourceImpl > xThis( this );
const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint );
const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint );
if( pViewHint )
{
switch( pViewHint->GetHintType() )
{
case SvxViewHint::SVX_HINT_VIEWCHANGED:
Broadcast( *pViewHint );
break;
}
}
else if( pSdrHint )
{
switch( pSdrHint->GetKind() )
{
case HINT_OBJCHG:
{
mbDataValid = sal_False; // Text muss neu geholt werden
if( HasView() )
{
// #104157# Update maTextOffset, object has changed
// #105196#, #105203#: Cannot call that // here,
// since TakeTextRect() (called from there) //
// changes outliner content.
// UpdateOutliner();
// #101029# Broadcast object changes, as they might change visible attributes
SvxViewHint aHint(SvxViewHint::SVX_HINT_VIEWCHANGED);
Broadcast( aHint );
}
break;
}
case HINT_BEGEDIT:
if( mpObject == pSdrHint->GetObject() )
{
//IAccessibility2 Implementation 2009-----, one EditSource object is created for each AccessibleCell, and it will monitor the hint.
// Once HINT_BEGEDIT is broadcast, each EditSource of AccessibleCell will handle it here and
// call below: mpView->GetTextEditOutliner()->SetNotifyHdl(), which will replace the Notifer for current
// editable cell. It is totally wrong. So add check here to avoid the incorrect replacement of notifer.
// To be safe, add accessibility check here because currently it only happen on the editsource of AccessibleCell
if (Application::IsAccessibilityEnabled())
{
if (mpObject && mpText)
{
sdr::table::SdrTableObj* pTableObj = PTR_CAST( sdr::table::SdrTableObj, mpObject );
if(pTableObj)
{
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 )
{
delete mpTextForwarder;
mpTextForwarder = NULL;
}
// register as listener - need to broadcast state change messages
if( mpView && mpView->GetTextEditOutliner() )
mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
// #104157# Only now we're really in edit mode
mbShapeIsEditMode = sal_True;
Broadcast( *pSdrHint );
}
break;
case HINT_ENDEDIT:
if( mpObject == pSdrHint->GetObject() )
{
Broadcast( *pSdrHint );
// #104157# We're no longer in edit mode
mbShapeIsEditMode = sal_False;
// remove as listener - outliner might outlive ourselves
if( mpView && mpView->GetTextEditOutliner() )
mpView->GetTextEditOutliner()->SetNotifyHdl( Link() );
// destroy view forwarder, OutlinerView no longer
// valid (no need for UpdateData(), it's been
// synched on SdrEndTextEdit)
delete mpViewForwarder;
mpViewForwarder = NULL;
// #100424# 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 = sal_False;
delete mpTextForwarder;
mpTextForwarder = NULL;
}
}
break;
case HINT_MODELCLEARED:
dispose();
break;
default:
break;
}
}
}
/* this is a callback from the attached SdrObject when it is actually deleted */
void SvxTextEditSourceImpl::ObjectInDestruction(const SdrObject&)
{
mpObject = 0;
dispose();
Broadcast( SfxSimpleHint( SFX_HINT_DYING ) );
}
/* unregister at all objects and set all references to 0 */
void SvxTextEditSourceImpl::dispose()
{
if( mpTextForwarder )
{
delete mpTextForwarder;
mpTextForwarder = 0;
}
if( mpViewForwarder )
{
delete mpViewForwarder;
mpViewForwarder = 0;
}
if( mpOutliner )
{
if( mpModel )
{
mpModel->disposeOutliner( mpOutliner );
}
else
{
delete mpOutliner;
}
mpOutliner = 0;
}
if( mpModel )
{
EndListening( *mpModel );
mpModel = 0;
}
if( mpView )
{
EndListening( *mpView );
mpView = 0;
}
if( mpObject )
{
mpObject->RemoveObjectUser( *this );
mpObject = 0;
}
mpWindow = 0;
}
//------------------------------------------------------------------------
void SvxTextEditSourceImpl::SetupOutliner()
{
// #101029#
// only for UAA edit source: setup outliner equivalently as in
// SdrTextObj::Paint(), such that formatting equals screen
// layout
if( mpObject && mpOutliner )
{
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
Rectangle aPaintRect;
if( pTextObj )
{
Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
pTextObj->SetupOutlinerFormatting( *mpOutliner, aPaintRect );
// #101029# calc text offset from shape anchor
maTextOffset = aPaintRect.TopLeft() - aBoundRect.TopLeft();
}
}
}
//------------------------------------------------------------------------
void SvxTextEditSourceImpl::UpdateOutliner()
{
// #104157#
// only for UAA edit source: update outliner equivalently as in
// SdrTextObj::Paint(), such that formatting equals screen
// layout
if( mpObject && mpOutliner )
{
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
Rectangle aPaintRect;
if( pTextObj )
{
Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
pTextObj->UpdateOutlinerFormatting( *mpOutliner, aPaintRect );
// #101029# calc text offset from shape anchor
maTextOffset = aPaintRect.TopLeft() - aBoundRect.TopLeft();
}
}
}
//------------------------------------------------------------------------
SvxTextForwarder* SvxTextEditSourceImpl::GetBackgroundTextForwarder()
{
sal_Bool bCreated = sal_False;
// #99840#: prevent EE/Outliner notifications during setup
mbNotificationsDisabled = sal_True;
if (!mpTextForwarder)
{
if( mpOutliner == NULL )
{
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
sal_uInt16 nOutlMode = OUTLINERMODE_TEXTOBJECT;
if( pTextObj && pTextObj->IsTextFrame() && pTextObj->GetTextKind() == OBJ_OUTLINETEXT )
nOutlMode = OUTLINERMODE_OUTLINEOBJECT;
mpOutliner = mpModel->createOutliner( nOutlMode );
// #109151# Do the setup after outliner creation, would be useless otherwise
if( HasView() )
{
// #101029#, #104157# Setup outliner _before_ filling it
SetupOutliner();
}
mpOutliner->SetTextObjNoInit( pTextObj );
/*
mpOutliner = SdrMakeOutliner( nOutlMode, pModel );
Outliner& aDrawOutliner = pModel->GetDrawOutliner();
mpOutliner->SetCalcFieldValueHdl( aDrawOutliner.GetCalcFieldValueHdl() );
*/
if( mbIsLocked )
{
((EditEngine*)&(mpOutliner->GetEditEngine()))->SetUpdateMode( sal_False );
mbOldUndoMode = ((EditEngine*)&(mpOutliner->GetEditEngine()))->IsUndoEnabled();
((EditEngine*)&(mpOutliner->GetEditEngine()))->EnableUndo( sal_False );
}
// -
if ( !m_xLinguServiceManager.is() )
{
css::uno::Reference< css::lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
m_xLinguServiceManager = css::uno::Reference< css::linguistic2::XLinguServiceManager >(
xMgr->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.linguistic2.LinguServiceManager" ))), css::uno::UNO_QUERY );
}
if ( m_xLinguServiceManager.is() )
{
css::uno::Reference< css::linguistic2::XHyphenator > xHyphenator( m_xLinguServiceManager->getHyphenator(), css::uno::UNO_QUERY );
if( xHyphenator.is() )
mpOutliner->SetHyphenator( xHyphenator );
}
// -
}
mpTextForwarder = new SvxOutlinerForwarder( *mpOutliner, (mpObject->GetObjInventor() == SdrInventor) && (mpObject->GetObjIdentifier() == OBJ_OUTLINETEXT) );
// delay listener subscription and UAA initialization until Outliner is fully setup
bCreated = sal_True;
mbForwarderIsEditMode = sal_False;
}
if( mpObject && mpText && !mbDataValid && mpObject->IsInserted() && mpObject->GetPage() )
{
mpTextForwarder->flushCache();
OutlinerParaObject* pOutlinerParaObject = NULL;
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
if( pTextObj && pTextObj->getActiveText() == mpText )
pOutlinerParaObject = pTextObj->GetEditOutlinerParaObject(); // Get the OutlinerParaObject if text edit is active
bool bOwnParaObj(false);
if( pOutlinerParaObject )
bOwnParaObj = true; // text edit active
else
pOutlinerParaObject = mpText->GetOutlinerParaObject();
if( pOutlinerParaObject && ( bOwnParaObj || !mpObject->IsEmptyPresObj() || mpObject->GetPage()->IsMasterPage() ) )
{
mpOutliner->SetText( *pOutlinerParaObject );
// #91254# put text to object and set EmptyPresObj to FALSE
if( mpText && bOwnParaObj && pOutlinerParaObject && mpObject->IsEmptyPresObj() && pTextObj->IsRealyEdited() )
{
mpObject->SetEmptyPresObj( sal_False );
static_cast< SdrTextObj* >( mpObject)->NbcSetOutlinerParaObjectForText( pOutlinerParaObject, mpText );
// #i103982# Here, due to mpObject->NbcSetOutlinerParaObjectForText, we LOSE ownership of the
// OPO, so do NOT delete it when leaving this method (!)
bOwnParaObj = false;
}
}
else
{
sal_Bool bVertical = pOutlinerParaObject ? pOutlinerParaObject->IsVertical() : sal_False;
// set objects style sheet on empty outliner
SfxStyleSheetPool* pPool = (SfxStyleSheetPool*)mpObject->GetModel()->GetStyleSheetPool();
if( pPool )
mpOutliner->SetStyleSheetPool( pPool );
SfxStyleSheet* pStyleSheet = mpObject->GetPage()->GetTextStyleSheetForObject( mpObject );
if( pStyleSheet )
mpOutliner->SetStyleSheet( 0, pStyleSheet );
if( bVertical )
mpOutliner->SetVertical( sal_True );
}
// evtually we have to set the border attributes
if (mpOutliner->GetParagraphCount()==1)
{
// if we only have one paragraph we check if it is empty
XubString aStr( mpOutliner->GetText( mpOutliner->GetParagraph( 0 ) ) );
if(!aStr.Len())
{
// its empty, so we have to force the outliner to initialise itself
mpOutliner->SetText( String(), mpOutliner->GetParagraph( 0 ) );
if(mpObject->GetStyleSheet())
mpOutliner->SetStyleSheet( 0, mpObject->GetStyleSheet());
}
}
mbDataValid = sal_True;
if( bOwnParaObj )
delete pOutlinerParaObject;
}
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) );
}
// #99840#: prevent EE/Outliner notifications during setup
mbNotificationsDisabled = sal_False;
return mpTextForwarder;
}
//------------------------------------------------------------------------
SvxTextForwarder* SvxTextEditSourceImpl::GetEditModeTextForwarder()
{
if( !mpTextForwarder && HasView() )
{
SdrOutliner* pEditOutliner = mpView->GetTextEditOutliner();
if( pEditOutliner )
{
mpTextForwarder = new SvxOutlinerForwarder( *pEditOutliner, (mpObject->GetObjInventor() == SdrInventor) && (mpObject->GetObjIdentifier() == OBJ_OUTLINETEXT) );
mbForwarderIsEditMode = sal_True;
}
}
return mpTextForwarder;
}
//------------------------------------------------------------------------
SvxTextForwarder* SvxTextEditSourceImpl::GetTextForwarder()
{
if( mbDestroyed || mpObject == NULL )
return NULL;
if( mpModel == NULL )
mpModel = mpObject->GetModel();
if( mpModel == NULL )
return NULL;
// 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)
if( HasView() )
{
if( IsEditMode() != mbForwarderIsEditMode )
{
// forwarder mismatch - create new
delete mpTextForwarder;
mpTextForwarder = NULL;
}
if( IsEditMode() )
return GetEditModeTextForwarder();
else
return GetBackgroundTextForwarder();
}
else
return GetBackgroundTextForwarder();
}
//------------------------------------------------------------------------
SvxDrawOutlinerViewForwarder* SvxTextEditSourceImpl::CreateViewForwarder()
{
if( mpView->GetTextEditOutlinerView() && mpObject )
{
// register as listener - need to broadcast state change messages
mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
if( pTextObj )
{
Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
OutlinerView& rOutlView = *mpView->GetTextEditOutlinerView();
return new SvxDrawOutlinerViewForwarder( rOutlView, aBoundRect.TopLeft() );
}
}
return NULL;
}
SvxEditViewForwarder* SvxTextEditSourceImpl::GetEditViewForwarder( sal_Bool bCreate )
{
if( mbDestroyed || mpObject == NULL )
return NULL;
if( mpModel == NULL )
mpModel = mpObject->GetModel();
if( mpModel == NULL )
return NULL;
// shall we delete?
if( mpViewForwarder )
{
if( !IsEditMode() )
{
// destroy all forwarders (no need for UpdateData(),
// it's been synched on SdrEndTextEdit)
delete mpViewForwarder;
mpViewForwarder = NULL;
}
}
// which to create? Directly in edit mode, create new, or none?
else if( mpView )
{
if( IsEditMode() )
{
// create new view forwarder
mpViewForwarder = CreateViewForwarder();
}
else if( bCreate )
{
// dispose old text forwarder
UpdateData();
delete mpTextForwarder;
mpTextForwarder = NULL;
// enter edit mode
mpView->SdrEndTextEdit();
if(mpView->SdrBeginTextEdit(mpObject, 0L, 0L, sal_False, (SdrOutliner*)0L, 0L, sal_False, sal_False))
{
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
if( pTextObj->IsTextEditActive() )
{
// create new view forwarder
mpViewForwarder = CreateViewForwarder();
}
else
{
// failure. Somehow, SdrBeginTextEdit did not set
// our SdrTextObj into edit mode
mpView->SdrEndTextEdit();
}
}
}
}
return mpViewForwarder;
}
//------------------------------------------------------------------------
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() )
{
if( mbIsLocked )
{
mbNeedsUpdate = sal_True;
}
else
{
if( mpOutliner && mpObject && mpText && !mbDestroyed )
{
SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mpObject );
if( pTextObj )
{
if( mpOutliner->GetParagraphCount() != 1 || mpOutliner->GetEditEngine().GetTextLen( 0 ) )
{
if( mpOutliner->GetParagraphCount() > 1 )
{
if( pTextObj && pTextObj->IsTextFrame() && pTextObj->GetTextKind() == OBJ_TITLETEXT )
{
while( mpOutliner->GetParagraphCount() > 1 )
{
ESelection aSel( 0,mpOutliner->GetEditEngine().GetTextLen( 0 ), 1,0 );
mpOutliner->QuickInsertLineBreak( aSel );
}
}
}
pTextObj->NbcSetOutlinerParaObjectForText( mpOutliner->CreateParaObject(), mpText );
}
else
{
pTextObj->NbcSetOutlinerParaObjectForText( NULL,mpText );
}
}
if( mpObject->IsEmptyPresObj() )
mpObject->SetEmptyPresObj(sal_False);
}
}
}
}
void SvxTextEditSourceImpl::lock()
{
mbIsLocked = sal_True;
if( mpOutliner )
{
((EditEngine*)&(mpOutliner->GetEditEngine()))->SetUpdateMode( sal_False );
mbOldUndoMode = ((EditEngine*)&(mpOutliner->GetEditEngine()))->IsUndoEnabled();
((EditEngine*)&(mpOutliner->GetEditEngine()))->EnableUndo( sal_False );
}
}
void SvxTextEditSourceImpl::unlock()
{
mbIsLocked = sal_False;
if( mbNeedsUpdate )
{
UpdateData();
mbNeedsUpdate = sal_False;
}
if( mpOutliner )
{
((EditEngine*)&(mpOutliner->GetEditEngine()))->SetUpdateMode( sal_True );
((EditEngine*)&(mpOutliner->GetEditEngine()))->EnableUndo( mbOldUndoMode );
}
}
sal_Bool SvxTextEditSourceImpl::IsValid() const
{
return mpView && mpWindow ? sal_True : sal_False;
}
Rectangle SvxTextEditSourceImpl::GetVisArea()
{
if( IsValid() )
{
SdrPaintWindow* pPaintWindow = mpView->FindPaintWindow(*mpWindow);
Rectangle aVisArea;
if(pPaintWindow)
{
aVisArea = pPaintWindow->GetVisibleArea();
}
// offset vis area by edit engine left-top position
SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, mpObject );
if( pTextObj )
{
Rectangle aAnchorRect;
pTextObj->TakeTextAnchorRect( aAnchorRect );
aVisArea.Move( -aAnchorRect.Left(), -aAnchorRect.Top() );
MapMode aMapMode(mpWindow->GetMapMode());
aMapMode.SetOrigin(Point());
return mpWindow->LogicToPixel( aVisArea, aMapMode );
}
}
return Rectangle();
}
Point SvxTextEditSourceImpl::LogicToPixel( const Point& rPoint, const MapMode& rMapMode )
{
// #101029#: 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(sal_False);
if( pForwarder )
return pForwarder->LogicToPixel( rPoint, rMapMode );
}
else if( IsValid() && mpModel )
{
// #101029#
Point aPoint1( rPoint );
aPoint1.X() += maTextOffset.X();
aPoint1.Y() += maTextOffset.Y();
Point aPoint2( OutputDevice::LogicToLogic( aPoint1, rMapMode,
MapMode(mpModel->GetScaleUnit()) ) );
MapMode aMapMode(mpWindow->GetMapMode());
aMapMode.SetOrigin(Point());
return mpWindow->LogicToPixel( aPoint2, aMapMode );
}
return Point();
}
Point SvxTextEditSourceImpl::PixelToLogic( const Point& rPoint, const MapMode& rMapMode )
{
// #101029#: 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(sal_False);
if( pForwarder )
return pForwarder->PixelToLogic( rPoint, rMapMode );
}
else if( IsValid() && mpModel )
{
MapMode aMapMode(mpWindow->GetMapMode());
aMapMode.SetOrigin(Point());
Point aPoint1( mpWindow->PixelToLogic( rPoint, aMapMode ) );
Point aPoint2( OutputDevice::LogicToLogic( aPoint1,
MapMode(mpModel->GetScaleUnit()),
rMapMode ) );
// #101029#
aPoint2.X() -= maTextOffset.X();
aPoint2.Y() -= maTextOffset.Y();
return aPoint2;
}
return Point();
}
IMPL_LINK(SvxTextEditSourceImpl, NotifyHdl, EENotify*, aNotify)
{
if( aNotify && !mbNotificationsDisabled )
{
::std::auto_ptr< SfxHint > aHint( SvxEditSourceHelper::EENotification2Hint( aNotify) );
if( aHint.get() )
Broadcast( *aHint.get() );
}
return 0;
}
//------------------------------------------------------------------------
// --------------------------------------------------------------------
// SvxTextEditSource
// --------------------------------------------------------------------
SvxTextEditSource::SvxTextEditSource( SdrObject* pObject, SdrText* pText, XInterface* pOwner )
{
mpImpl = new SvxTextEditSourceImpl( pObject, pText, pOwner );
mpImpl->acquire();
}
// --------------------------------------------------------------------
SvxTextEditSource::SvxTextEditSource( SdrObject& rObj, SdrText* pText, SdrView& rView, const Window& rWindow )
{
mpImpl = new SvxTextEditSourceImpl( rObj, pText, rView, rWindow );
mpImpl->acquire();
}
// --------------------------------------------------------------------
SvxTextEditSource::SvxTextEditSource( SvxTextEditSourceImpl* pImpl )
{
mpImpl = pImpl;
mpImpl->acquire();
}
//------------------------------------------------------------------------
SvxTextEditSource::~SvxTextEditSource()
{
OGuard aGuard( Application::GetSolarMutex() );
mpImpl->release();
}
//------------------------------------------------------------------------
SvxEditSource* SvxTextEditSource::Clone() const
{
return new SvxTextEditSource( mpImpl );
}
//------------------------------------------------------------------------
SvxTextForwarder* SvxTextEditSource::GetTextForwarder()
{
return mpImpl->GetTextForwarder();
}
//------------------------------------------------------------------------
SvxEditViewForwarder* SvxTextEditSource::GetEditViewForwarder( sal_Bool bCreate )
{
return mpImpl->GetEditViewForwarder( bCreate );
}
//------------------------------------------------------------------------
SvxViewForwarder* SvxTextEditSource::GetViewForwarder()
{
return this;
}
//------------------------------------------------------------------------
void SvxTextEditSource::UpdateData()
{
mpImpl->UpdateData();
}
SfxBroadcaster& SvxTextEditSource::GetBroadcaster() const
{
return *mpImpl;
}
SdrObject* SvxTextEditSource::GetSdrObject() const
{
return mpImpl->GetSdrObject();
}
void SvxTextEditSource::lock()
{
mpImpl->lock();
}
void SvxTextEditSource::unlock()
{
mpImpl->unlock();
}
sal_Bool SvxTextEditSource::IsValid() const
{
return mpImpl->IsValid();
}
Rectangle SvxTextEditSource::GetVisArea() const
{
return mpImpl->GetVisArea();
}
Point SvxTextEditSource::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
{
return mpImpl->LogicToPixel( rPoint, rMapMode );
}
Point SvxTextEditSource::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
{
return mpImpl->PixelToLogic( rPoint, rMapMode );
}
void SvxTextEditSource::addRange( SvxUnoTextRangeBase* pNewRange )
{
mpImpl->addRange( pNewRange );
}
void SvxTextEditSource::removeRange( SvxUnoTextRangeBase* pOldRange )
{
mpImpl->removeRange( pOldRange );
}
const SvxUnoTextRangeBaseList& SvxTextEditSource::getRanges() const
{
return mpImpl->getRanges();
}
void SvxTextEditSource::ChangeModel( SdrModel* pNewModel )
{
mpImpl->ChangeModel( pNewModel );
}
void SvxTextEditSource::UpdateOutliner()
{
mpImpl->UpdateOutliner();
}