tdf#123968 sw: use inline editing for input fields for variables
* set these to RES_TXTATR_INPUTFIELD so they get a SwTextInputField
* only for string fields, the numeric ones break when editing
* SwCursorShell::CursorInsideInputField() must check type of the hint,
not type of the field
* DocumentFieldsManager::UpdateExpFieldsImpl() is called with one field
when it is inserted, and must expand the field into the SwTextNode then,
and it's called when the user edits inside the field, and must *not*
expand the field into the SwTextNode then
* SwDocUpdateField::MakeFieldList_() must iterate RES_TXTATR_INPUTFIELD
* SwEditWin::MouseButtonDown() must still pop up the edit dialog on
double-click
* SetFieldsDirty() should check RES_TXTATR_INPUTFIELD because SwGetExp
may depend on them
* a very odd API design: SwSetExpField::PutValue() allows to change the
"InputFlag", which is actually used by the ODF import!
This needs some alchemy to convert between SwTextField and
SwTextInputField hints, see SwXTextField::TransmuteLeadToInputField().
* etc.
Change-Id: I45c471703f102ebcb04b8567f9d76c17d5a063e7
Co-authored-by: Samuel Mehrbrodt <Samuel.Mehrbrodt@cib.de>
Reviewed-on: https://gerrit.libreoffice.org/69414
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx
index cfe9ef0..fb105b0 100644
--- a/sw/inc/crsrsh.hxx
+++ b/sw/inc/crsrsh.hxx
@@ -701,6 +701,9 @@ public:
static SwTextField* GetTextFieldAtPos(
const SwPosition* pPos,
const bool bIncludeInputFieldAtStart );
static SwTextField* GetTextFieldAtCursor(
const SwPaM* pCursor,
const bool bIncludeInputFieldAtStart );
static SwField* GetFieldAtCursor(
const SwPaM* pCursor,
const bool bIncludeInputFieldAtStart );
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
index bf45898..a2c13b8 100644
--- a/sw/source/core/crsr/crstrvl.cxx
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -893,11 +893,11 @@ SwTextField * SwCursorShell::GetTextFieldAtPos(
return pTextField;
}
SwField* SwCursorShell::GetFieldAtCursor(
SwTextField* SwCursorShell::GetTextFieldAtCursor(
const SwPaM* pCursor,
const bool bIncludeInputFieldAtStart )
{
SwField* pFieldAtCursor = nullptr;
SwTextField* pFieldAtCursor = nullptr;
SwTextField* pTextField = GetTextFieldAtPos( pCursor->Start(), bIncludeInputFieldAtStart );
if ( pTextField != nullptr
@@ -909,13 +909,23 @@ SwField* SwCursorShell::GetFieldAtCursor(
: 1;
if ( ( pCursor->End()->nContent.GetIndex() - pCursor->Start()->nContent.GetIndex() ) <= nTextFieldLength )
{
pFieldAtCursor = const_cast<SwField*>(pTextField->GetFormatField().GetField());
pFieldAtCursor = pTextField;
}
}
return pFieldAtCursor;
}
SwField* SwCursorShell::GetFieldAtCursor(
const SwPaM *const pCursor,
const bool bIncludeInputFieldAtStart)
{
SwTextField *const pField(GetTextFieldAtCursor(pCursor, bIncludeInputFieldAtStart));
return pField
? const_cast<SwField*>(pField->GetFormatField().GetField())
: nullptr;
}
SwField* SwCursorShell::GetCurField( const bool bIncludeInputFieldAtStart ) const
{
SwPaM* pCursor = GetCursor();
@@ -941,7 +951,7 @@ bool SwCursorShell::CursorInsideInputField() const
{
for(SwPaM& rCursor : GetCursor()->GetRingContainer())
{
if(dynamic_cast<const SwInputField*>(GetFieldAtCursor( &rCursor, false )))
if (dynamic_cast<const SwTextInputField*>(GetTextFieldAtCursor(&rCursor, false)))
return true;
}
return false;
diff --git a/sw/source/core/doc/DocumentFieldsManager.cxx b/sw/source/core/doc/DocumentFieldsManager.cxx
index bbb8b0d..9f05f74 100644
--- a/sw/source/core/doc/DocumentFieldsManager.cxx
+++ b/sw/source/core/doc/DocumentFieldsManager.cxx
@@ -55,6 +55,7 @@
#include <pam.hxx>
#include <o3tl/deleter.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <comphelper/scopeguard.hxx>
#include <com/sun/star/uno/Any.hxx>
using namespace ::com::sun::star::uno;
@@ -1265,7 +1266,26 @@ void DocumentFieldsManager::UpdateExpFieldsImpl(
default: break;
} // switch
pFormatField->ModifyNotification( nullptr, nullptr ); // trigger formatting
{
// avoid calling ReplaceText() for input fields, it is pointless
// here and moves the cursor if it's inside the field ...
SwTextInputField *const pInputField(
pUpdateField == pTextField // ... except once, when the dialog
? nullptr // is used to change content via UpdateOneField()
: dynamic_cast<SwTextInputField *>(pTextField));
if (pInputField)
{
pInputField->LockNotifyContentChange();
}
::comphelper::ScopeGuard g([pInputField]()
{
if (pInputField)
{
pInputField->UnlockNotifyContentChange();
}
});
pFormatField->ModifyNotification(nullptr, nullptr); // trigger formatting
}
if (pUpdateField == pTextField) // if only this one is updated
{
@@ -1407,7 +1427,8 @@ bool DocumentFieldsManager::SetFieldsDirty( bool b, const SwNode* pChk, sal_uLon
for( size_t n = 0 ; n < nEnd; ++n )
{
const SwTextAttr* pAttr = pTNd->GetSwpHints().Get(n);
if ( pAttr->Which() == RES_TXTATR_FIELD )
if ( pAttr->Which() == RES_TXTATR_FIELD
|| pAttr->Which() == RES_TXTATR_INPUTFIELD)
{
b = true;
break;
diff --git a/sw/source/core/doc/docfld.cxx b/sw/source/core/doc/docfld.cxx
index f9327a7..a9c3f25 100644
--- a/sw/source/core/doc/docfld.cxx
+++ b/sw/source/core/doc/docfld.cxx
@@ -450,53 +450,56 @@ void SwDoc::GetAllUsedDB( std::vector<OUString>& rDBNameList,
}
}
sal_uInt32 nMaxItems = GetAttrPool().GetItemCount2( RES_TXTATR_FIELD );
for (sal_uInt32 n = 0; n < nMaxItems; ++n)
for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
{
const SfxPoolItem* pItem;
if( nullptr == (pItem = GetAttrPool().GetItem2( RES_TXTATR_FIELD, n ) ))
continue;
const SwFormatField* pFormatField = static_cast<const SwFormatField*>(pItem);
const SwTextField* pTextField = pFormatField->GetTextField();
if( !pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes() )
continue;
const SwField* pField = pFormatField->GetField();
switch( pField->GetTyp()->Which() )
sal_uInt32 nMaxItems = GetAttrPool().GetItemCount2(nWhichHint);
for (sal_uInt32 n = 0; n < nMaxItems; ++n)
{
case SwFieldIds::Database:
AddUsedDBToList( rDBNameList,
const SfxPoolItem *const pItem(GetAttrPool().GetItem2(nWhichHint, n));
if (nullptr == pItem)
continue;
const SwFormatField* pFormatField = static_cast<const SwFormatField*>(pItem);
const SwTextField* pTextField = pFormatField->GetTextField();
if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes())
continue;
const SwField* pField = pFormatField->GetField();
switch (pField->GetTyp()->Which())
{
case SwFieldIds::Database:
AddUsedDBToList( rDBNameList,
lcl_DBDataToString(static_cast<const SwDBField*>(pField)->GetDBData() ));
break;
break;
case SwFieldIds::DbSetNumber:
case SwFieldIds::DatabaseName:
AddUsedDBToList( rDBNameList,
case SwFieldIds::DbSetNumber:
case SwFieldIds::DatabaseName:
AddUsedDBToList( rDBNameList,
lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
break;
break;
case SwFieldIds::DbNumSet:
case SwFieldIds::DbNextSet:
AddUsedDBToList( rDBNameList,
case SwFieldIds::DbNumSet:
case SwFieldIds::DbNextSet:
AddUsedDBToList( rDBNameList,
lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
[[fallthrough]]; // JP: is that right like that?
[[fallthrough]]; // JP: is that right like that?
case SwFieldIds::HiddenText:
case SwFieldIds::HiddenPara:
AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
case SwFieldIds::HiddenText:
case SwFieldIds::HiddenPara:
AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
pField->GetPar1(), aUsedDBNames ));
aUsedDBNames.clear();
break;
aUsedDBNames.clear();
break;
case SwFieldIds::SetExp:
case SwFieldIds::GetExp:
case SwFieldIds::Table:
AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
case SwFieldIds::SetExp:
case SwFieldIds::GetExp:
case SwFieldIds::Table:
AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
pField->GetFormula(), aUsedDBNames ));
aUsedDBNames.clear();
break;
default: break;
aUsedDBNames.clear();
break;
default: break;
}
}
}
#endif
@@ -608,79 +611,82 @@ void SwDoc::ChangeDBFields( const std::vector<OUString>& rOldNames,
}
}
sal_uInt32 nMaxItems = GetAttrPool().GetItemCount2( RES_TXTATR_FIELD );
for (sal_uInt32 n = 0; n < nMaxItems; ++n )
for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
{
const SfxPoolItem* pItem = GetAttrPool().GetItem2( RES_TXTATR_FIELD, n );
if( !pItem )
continue;
sal_uInt32 nMaxItems = GetAttrPool().GetItemCount2(nWhichHint);
SwFormatField* pFormatField = const_cast<SwFormatField*>(static_cast<const SwFormatField*>(pItem));
SwTextField* pTextField = pFormatField->GetTextField();
if( !pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes() )
continue;
SwField* pField = pFormatField->GetField();
bool bExpand = false;
switch( pField->GetTyp()->Which() )
for (sal_uInt32 n = 0; n < nMaxItems; ++n)
{
case SwFieldIds::Database:
#if HAVE_FEATURE_DBCONNECTIVITY
if( IsNameInArray( rOldNames, lcl_DBDataToString(static_cast<SwDBField*>(pField)->GetDBData())))
{
SwDBFieldType* pOldTyp = static_cast<SwDBFieldType*>(pField->GetTyp());
const SfxPoolItem* pItem = GetAttrPool().GetItem2(nWhichHint, n);
if (!pItem)
continue;
SwDBFieldType* pTyp = static_cast<SwDBFieldType*>(getIDocumentFieldsAccess().InsertFieldType(
SwFormatField* pFormatField = const_cast<SwFormatField*>(static_cast<const SwFormatField*>(pItem));
SwTextField* pTextField = pFormatField->GetTextField();
if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes())
continue;
SwField* pField = pFormatField->GetField();
bool bExpand = false;
switch( pField->GetTyp()->Which() )
{
case SwFieldIds::Database:
#if HAVE_FEATURE_DBCONNECTIVITY
if (IsNameInArray(rOldNames, lcl_DBDataToString(static_cast<SwDBField*>(pField)->GetDBData())))
{
SwDBFieldType* pOldTyp = static_cast<SwDBFieldType*>(pField->GetTyp());
SwDBFieldType* pTyp = static_cast<SwDBFieldType*>(getIDocumentFieldsAccess().InsertFieldType(
SwDBFieldType(this, pOldTyp->GetColumnName(), aNewDBData)));
pFormatField->RegisterToFieldType( *pTyp );
pField->ChgTyp(pTyp);
pFormatField->RegisterToFieldType( *pTyp );
pField->ChgTyp(pTyp);
static_cast<SwDBField*>(pField)->ClearInitialized();
static_cast<SwDBField*>(pField)->InitContent();
static_cast<SwDBField*>(pField)->ClearInitialized();
static_cast<SwDBField*>(pField)->InitContent();
bExpand = true;
}
bExpand = true;
}
#endif
break;
break;
case SwFieldIds::DbSetNumber:
case SwFieldIds::DatabaseName:
if( IsNameInArray( rOldNames,
case SwFieldIds::DbSetNumber:
case SwFieldIds::DatabaseName:
if (IsNameInArray(rOldNames,
lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
{
static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
{
static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
bExpand = true;
}
break;
case SwFieldIds::DbNumSet:
case SwFieldIds::DbNextSet:
if (IsNameInArray(rOldNames,
lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
{
static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
}
[[fallthrough]];
case SwFieldIds::HiddenText:
case SwFieldIds::HiddenPara:
pField->SetPar1( ReplaceUsedDBs(rOldNames, rNewName, pField->GetPar1()) );
bExpand = true;
}
break;
break;
case SwFieldIds::DbNumSet:
case SwFieldIds::DbNextSet:
if( IsNameInArray( rOldNames,
lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
{
static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
}
[[fallthrough]];
case SwFieldIds::HiddenText:
case SwFieldIds::HiddenPara:
pField->SetPar1( ReplaceUsedDBs(rOldNames, rNewName, pField->GetPar1()) );
bExpand = true;
break;
case SwFieldIds::SetExp:
case SwFieldIds::GetExp:
case SwFieldIds::Table:
pField->SetPar2( ReplaceUsedDBs(rOldNames, rNewName, pField->GetFormula()) );
bExpand = true;
break;
default: break;
}
case SwFieldIds::SetExp:
case SwFieldIds::GetExp:
case SwFieldIds::Table:
pField->SetPar2( ReplaceUsedDBs(rOldNames, rNewName, pField->GetFormula()) );
bExpand = true;
break;
default: break;
if (bExpand)
pTextField->ExpandTextField( true );
}
if (bExpand)
pTextField->ExpandTextField( true );
}
getIDocumentState().SetModified();
#endif
@@ -891,113 +897,118 @@ void SwDocUpdateField::MakeFieldList_( SwDoc& rDoc, int eGetMode )
bool bIsDBManager = nullptr != rDoc.GetDBManager();
#endif
const sal_uInt32 nMaxItems = rDoc.GetAttrPool().GetItemCount2( RES_TXTATR_FIELD );
for( sal_uInt32 n = 0; n < nMaxItems; ++n )
for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
{
const SfxPoolItem* pItem = rDoc.GetAttrPool().GetItem2( RES_TXTATR_FIELD, n );
if( !pItem )
continue;
const SwFormatField* pFormatField = static_cast<const SwFormatField*>(pItem);
const SwTextField* pTextField = pFormatField->GetTextField();
if( !pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes() )
continue;
OUString sFormula;
const SwField* pField = pFormatField->GetField();
const SwFieldIds nWhich = pField->GetTyp()->Which();
switch( nWhich )
const sal_uInt32 nMaxItems = rDoc.GetAttrPool().GetItemCount2(nWhichHint);
for (sal_uInt32 n = 0; n < nMaxItems; ++n)
{
case SwFieldIds::DbSetNumber:
case SwFieldIds::GetExp:
if( GETFLD_ALL == eGetMode )
sFormula = sTrue;
break;
const SfxPoolItem* pItem = rDoc.GetAttrPool().GetItem2(nWhichHint, n);
if (!pItem)
continue;
case SwFieldIds::Database:
if( GETFLD_EXPAND & eGetMode )
sFormula = sTrue;
break;
const SwFormatField* pFormatField = static_cast<const SwFormatField*>(pItem);
const SwTextField* pTextField = pFormatField->GetTextField();
if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes())
continue;
case SwFieldIds::SetExp:
if ( (eGetMode != GETFLD_EXPAND) ||
(nsSwGetSetExpType::GSE_STRING & pField->GetSubType()) )
{
sFormula = sTrue;
}
break;
OUString sFormula;
const SwField* pField = pFormatField->GetField();
const SwFieldIds nWhich = pField->GetTyp()->Which();
switch (nWhich)
{
case SwFieldIds::DbSetNumber:
case SwFieldIds::GetExp:
if (GETFLD_ALL == eGetMode)
sFormula = sTrue;
break;
case SwFieldIds::HiddenPara:
if( GETFLD_ALL == eGetMode )
{
sFormula = pField->GetPar1();
if (sFormula.isEmpty() || sFormula==sFalse)
const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( false );
else if (sFormula==sTrue)
const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( true );
else
break;
case SwFieldIds::Database:
if (GETFLD_EXPAND & eGetMode)
sFormula = sTrue;
break;
sFormula.clear();
// trigger formatting
const_cast<SwFormatField*>(pFormatField)->ModifyNotification( nullptr, nullptr );
}
break;
case SwFieldIds::SetExp:
if ((eGetMode != GETFLD_EXPAND) ||
(nsSwGetSetExpType::GSE_STRING & pField->GetSubType()))
{
sFormula = sTrue;
}
break;
case SwFieldIds::HiddenText:
if( GETFLD_ALL == eGetMode )
{
sFormula = pField->GetPar1();
if (sFormula.isEmpty() || sFormula==sFalse)
const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( true );
else if (sFormula==sTrue)
const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( false );
else
break;
case SwFieldIds::HiddenPara:
if (GETFLD_ALL == eGetMode)
{
sFormula = pField->GetPar1();
if (sFormula.isEmpty() || sFormula==sFalse)
const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( false );
else if (sFormula==sTrue)
const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( true );
else
break;
sFormula.clear();
sFormula.clear();
// trigger formatting
const_cast<SwFormatField*>(pFormatField)->ModifyNotification( nullptr, nullptr );
}
break;
// evaluate field
const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->Evaluate(&rDoc);
// trigger formatting
const_cast<SwFormatField*>(pFormatField)->ModifyNotification( nullptr, nullptr );
}
break;
case SwFieldIds::HiddenText:
if (GETFLD_ALL == eGetMode)
{
sFormula = pField->GetPar1();
if (sFormula.isEmpty() || sFormula==sFalse)
const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( true );
else if (sFormula==sTrue)
const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( false );
else
break;
sFormula.clear();
// evaluate field
const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->Evaluate(&rDoc);
// trigger formatting
const_cast<SwFormatField*>(pFormatField)->ModifyNotification( nullptr, nullptr );
}
break;
#if HAVE_FEATURE_DBCONNECTIVITY
case SwFieldIds::DbNumSet:
{
SwDBData aDBData(const_cast<SwDBNumSetField*>(static_cast<const SwDBNumSetField*>(pField))->GetDBData(&rDoc));
if (
(bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand)) &&
(GETFLD_ALL == eGetMode || (GETFLD_CALC & eGetMode && static_cast<const SwDBNumSetField*>(pField)->IsCondValid()))
)
case SwFieldIds::DbNumSet:
{
sFormula = pField->GetPar1();
}
}
break;
case SwFieldIds::DbNextSet:
{
SwDBData aDBData(const_cast<SwDBNextSetField*>(static_cast<const SwDBNextSetField*>(pField))->GetDBData(&rDoc));
SwDBData aDBData(const_cast<SwDBNumSetField*>(static_cast<const SwDBNumSetField*>(pField))->GetDBData(&rDoc));
if (
(bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand)) &&
(GETFLD_ALL == eGetMode || (GETFLD_CALC & eGetMode && static_cast<const SwDBNextSetField*>(pField)->IsCondValid()))
)
{
sFormula = pField->GetPar1();
if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand))
&& (GETFLD_ALL == eGetMode
|| (GETFLD_CALC & eGetMode
&& static_cast<const SwDBNumSetField*>(pField)->IsCondValid()))
)
{
sFormula = pField->GetPar1();
}
}
}
break;
break;
case SwFieldIds::DbNextSet:
{
SwDBData aDBData(const_cast<SwDBNextSetField*>(static_cast<const SwDBNextSetField*>(pField))->GetDBData(&rDoc));
if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand))
&& (GETFLD_ALL == eGetMode
|| (GETFLD_CALC & eGetMode
&& static_cast<const SwDBNextSetField*>(pField)->IsCondValid()))
)
{
sFormula = pField->GetPar1();
}
}
break;
#endif
default: break;
}
default: break;
}
if (!sFormula.isEmpty())
{
GetBodyNode( *pTextField, nWhich );
if (!sFormula.isEmpty())
{
GetBodyNode( *pTextField, nWhich );
}
}
}
m_nFieldListGetMode = eGetMode;
diff --git a/sw/source/core/edit/edfld.cxx b/sw/source/core/edit/edfld.cxx
index 787eb13..8b744bf 100644
--- a/sw/source/core/edit/edfld.cxx
+++ b/sw/source/core/edit/edfld.cxx
@@ -173,7 +173,11 @@ static SwTextField* lcl_FindInputField( SwDoc* pDoc, SwField& rField )
{
// Search field via its address. For input fields this needs to be done in protected fields.
SwTextField* pTField = nullptr;
if( SwFieldIds::Input == rField.Which() )
if (SwFieldIds::Input == rField.Which()
|| (SwFieldIds::SetExp == rField.Which()
&& static_cast<SwSetExpField&>(rField).GetInputFlag()
&& (static_cast<SwSetExpFieldType*>(rField.GetTyp())->GetType()
& nsSwGetSetExpType::GSE_STRING)))
{
const sal_uInt32 nMaxItems =
pDoc->GetAttrPool().GetItemCount2( RES_TXTATR_INPUTFIELD );
diff --git a/sw/source/core/fields/expfld.cxx b/sw/source/core/fields/expfld.cxx
index b12ae69..7bcfd44 100644
--- a/sw/source/core/fields/expfld.cxx
+++ b/sw/source/core/fields/expfld.cxx
@@ -1128,7 +1128,21 @@ bool SwSetExpField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
mnSubType &= (~nsSwExtendedSubType::SUB_CMD);
break;
case FIELD_PROP_BOOL1:
SetInputFlag(*o3tl::doAccess<bool>(rAny));
{
bool newInput(*o3tl::doAccess<bool>(rAny));
if (newInput != GetInputFlag())
{
if (static_cast<SwSetExpFieldType*>(GetTyp())->GetType()
& nsSwGetSetExpType::GSE_STRING)
{
SwXTextField::TransmuteLeadToInputField(*this);
}
else
{
SetInputFlag(newInput);
}
}
}
break;
case FIELD_PROP_PAR4:
{
diff --git a/sw/source/core/inc/unofield.hxx b/sw/source/core/inc/unofield.hxx
index b54480c..723b954 100644
--- a/sw/source/core/inc/unofield.hxx
+++ b/sw/source/core/inc/unofield.hxx
@@ -139,6 +139,8 @@ private:
public:
SwServiceType GetServiceId() const;
static void TransmuteLeadToInputField(SwSetExpField & rField);
/// @return an SwXTextField, either an already existing one or a new one
static css::uno::Reference< css::text::XTextField>
CreateXTextField(SwDoc * pDoc, SwFormatField const* pFormat,
diff --git a/sw/source/core/txtnode/atrfld.cxx b/sw/source/core/txtnode/atrfld.cxx
index 87d6ca5..66ccc26 100644
--- a/sw/source/core/txtnode/atrfld.cxx
+++ b/sw/source/core/txtnode/atrfld.cxx
@@ -69,7 +69,16 @@ SwFormatField::SwFormatField( const SwField &rField )
else if (mpField->GetTyp()->Which() == SwFieldIds::SetExp)
{
// see SwWrtShell::StartInputFieldDlg
static_cast<SwSetExpField *>(mpField.get())->SetFormatField(*this);
SwSetExpField *const pSetField(static_cast<SwSetExpField *>(mpField.get()));
if (pSetField->GetInputFlag()
// only for string fields for now - inline editing of number fields
// tends to produce error messages...
&& (static_cast<SwSetExpFieldType*>(pSetField->GetTyp())->GetType()
& nsSwGetSetExpType::GSE_STRING))
{
SetWhich( RES_TXTATR_INPUTFIELD );
}
pSetField->SetFormatField(*this);
}
else if ( mpField->GetTyp()->Which() == SwFieldIds::Postit )
{
@@ -103,8 +112,15 @@ SwFormatField::SwFormatField( const SwFormatField& rAttr )
}
else if (mpField->GetTyp()->Which() == SwFieldIds::SetExp)
{
SwSetExpField *const pSetField(static_cast<SwSetExpField *>(mpField.get()));
if (pSetField->GetInputFlag()
&& (static_cast<SwSetExpFieldType*>(pSetField->GetTyp())->GetType()
& nsSwGetSetExpType::GSE_STRING))
{
SetWhich( RES_TXTATR_INPUTFIELD );
}
// see SwWrtShell::StartInputFieldDlg
static_cast<SwSetExpField *>(mpField.get())->SetFormatField(*this);
pSetField->SetFormatField(*this);
}
else if ( mpField->GetTyp()->Which() == SwFieldIds::Postit )
{
@@ -543,6 +559,7 @@ SwTextInputField::~SwTextInputField()
void SwTextInputField::LockNotifyContentChange()
{
assert(!m_bLockNotifyContentChange); // not nestable
m_bLockNotifyContentChange = true;
}
@@ -582,9 +599,19 @@ void SwTextInputField::UpdateFieldContent()
const sal_Int32 nLen = static_cast<sal_Int32>(std::max<sal_Int32>( 0, ( (*End()) - 1 - nIdx ) ));
const OUString aNewFieldContent = GetTextNode().GetExpandText(nullptr, nIdx, nLen);
auto pInputField = dynamic_cast<const SwInputField*>(GetFormatField().GetField());
assert(pInputField);
const_cast<SwInputField*>(pInputField)->applyFieldContent( aNewFieldContent );
const SwField* pField = GetFormatField().GetField();
const SwInputField* pInputField = dynamic_cast<const SwInputField*>(pField);
if (pInputField)
const_cast<SwInputField*>(pInputField)->applyFieldContent( aNewFieldContent );
const SwSetExpField* pExpField = dynamic_cast<const SwSetExpField*>(pField);
if (pExpField)
{
assert(pExpField->GetInputFlag());
const_cast<SwSetExpField*>(pExpField)->SetPar2(aNewFieldContent);
}
assert(pInputField || pExpField);
// trigger update of fields for scenarios in which the Input Field's content is part of e.g. a table formula
GetTextNode().GetDoc()->getIDocumentFieldsAccess().GetUpdateFields().SetFieldsDirty(true);
}
diff --git a/sw/source/core/unocore/unofield.cxx b/sw/source/core/unocore/unofield.cxx
index 5bf5393..4bb314c 100644
--- a/sw/source/core/unocore/unofield.cxx
+++ b/sw/source/core/unocore/unofield.cxx
@@ -1269,6 +1269,66 @@ SwServiceType SwXTextField::GetServiceId() const
return m_pImpl->m_nServiceId;
}
/** Convert between SwSetExpField with InputFlag false and InputFlag true.
Unfortunately the InputFlag is exposed in the API as "Input" property
and is mutable; in the UI and in ODF these are 2 different types of
fields, so the API design is very questionable.
In order to keep the mutable property, the whole thing has to be
reconstructed from scratch, to replace the SwTextField hint with
SwTextInputField or vice versa.
The SwFormatField will be replaced - it must be, because the Which
changes - but the SwXTextField *must not* be disposed in the operation,
it has to be disconnected first and at the end connected to the
new instance!
*/
void SwXTextField::TransmuteLeadToInputField(SwSetExpField & rField)
{
assert(rField.GetFormatField()->Which() == (rField.GetInputFlag() ? RES_TXTATR_INPUTFIELD : RES_TXTATR_FIELD));
uno::Reference<text::XTextField> const xField(
rField.GetFormatField()->GetXTextField());
SwXTextField *const pXField = xField.is()
? reinterpret_cast<SwXTextField*>(
sal::static_int_cast<sal_IntPtr>(
uno::Reference<lang::XUnoTunnel>(xField, uno::UNO_QUERY_THROW)
->getSomething(getUnoTunnelId())))
: nullptr;
if (xField.is())
{
assert(pXField->m_pImpl->m_pFormatField == rField.GetFormatField());
rField.GetFormatField()->Remove(pXField->m_pImpl.get());
pXField->m_pImpl->m_pFormatField = nullptr;
}
SwTextField *const pOldAttr(rField.GetFormatField()->GetTextField());
SwSetExpField tempField(rField);
tempField.SetInputFlag(!rField.GetInputFlag());
SwFormatField tempFormat(tempField);
assert(tempFormat.GetField() != &rField);
assert(tempFormat.GetField() != &tempField); // this copies it again?
assert(tempFormat.Which() == (static_cast<SwSetExpField const*>(tempFormat.GetField())->GetInputFlag() ? RES_TXTATR_INPUTFIELD : RES_TXTATR_FIELD));
SwTextNode & rNode(pOldAttr->GetTextNode());
std::shared_ptr<SwPaM> pPamForTextField;
IDocumentContentOperations & rIDCO(rNode.GetDoc()->getIDocumentContentOperations());
SwTextField::GetPamForTextField(*pOldAttr, pPamForTextField);
assert(pPamForTextField);
sal_Int32 const nStart(pPamForTextField->Start()->nContent.GetIndex());
rIDCO.DeleteAndJoin(*pPamForTextField);
// ATTENTION: rField is dead now! hope nobody accesses it...
bool bSuccess = rIDCO.InsertPoolItem(*pPamForTextField, tempFormat);
assert(bSuccess);
(void) bSuccess;
SwTextField const* pNewAttr(rNode.GetFieldTextAttrAt(nStart, true));
assert(pNewAttr);
SwFormatField const& rNewFormat(pNewAttr->GetFormatField());
assert(rNewFormat.Which() == (static_cast<SwSetExpField const*>(rNewFormat.GetField())->GetInputFlag() ? RES_TXTATR_INPUTFIELD : RES_TXTATR_FIELD));
assert(static_cast<SwSetExpField const*>(rNewFormat.GetField())->GetInputFlag() == (dynamic_cast<SwTextInputField const*>(pNewAttr) != nullptr));
if (xField.is())
{
pXField->m_pImpl->m_pFormatField = &rNewFormat;
const_cast<SwFormatField&>(rNewFormat).Add(pXField->m_pImpl.get());
const_cast<SwFormatField&>(rNewFormat).SetXTextField(xField);
}
}
void SAL_CALL SwXTextField::attachTextFieldMaster(
const uno::Reference< beans::XPropertySet > & xFieldMaster)
{
@@ -1965,6 +2025,8 @@ void SAL_CALL SwXTextField::attach(
assert(m_pImpl->m_pFormatField);
m_pImpl->m_pDoc = pDoc;
const_cast<SwFormatField *>(m_pImpl->m_pFormatField)->Add(m_pImpl.get());
const_cast<SwFormatField *>(m_pImpl->m_pFormatField)->SetXTextField(this);
m_pImpl->m_wThis = *this;
m_pImpl->m_bIsDescriptor = false;
m_pImpl->ClearFieldType();
m_pImpl->m_pProps.reset();
diff --git a/sw/source/ui/fldui/fldedt.cxx b/sw/source/ui/fldui/fldedt.cxx
index f94aef3..d4108ef 100644
--- a/sw/source/ui/fldui/fldedt.cxx
+++ b/sw/source/ui/fldui/fldedt.cxx
@@ -57,6 +57,19 @@ void SwFieldEditDlg::EnsureSelection(SwField *pCurField, SwFieldMgr &rMgr)
{
pSh->GotoField( *(pInputField->GetFormatField()) );
}
else
{
SwSetExpField *const pSetField(dynamic_cast<SwSetExpField*>(pCurField));
if (pSetField)
{
assert(pSetField->GetFormatField());
pSh->GotoField( *(pSetField->GetFormatField()) );
}
else
{
assert(!"what input field is this");
}
}
}
/* Only create selection if there is none already.
diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx
index 59b0114..4d932c8 100644
--- a/sw/source/uibase/docvw/edtwin.cxx
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -3337,6 +3337,7 @@ void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt)
break;
case TYP_INPUTFLD:
case TYP_DROPDOWN:
case TYP_SETINPFLD:
pVFrame->GetBindings().Execute(FN_UPDATE_INPUTFIELDS);
break;
default:
diff --git a/sw/source/uibase/shells/textfld.cxx b/sw/source/uibase/shells/textfld.cxx
index 9f80973..ec6d8b5 100644
--- a/sw/source/uibase/shells/textfld.cxx
+++ b/sw/source/uibase/shells/textfld.cxx
@@ -27,6 +27,7 @@
#include <editeng/outliner.hxx>
#include <sfx2/lnkbase.hxx>
#include <fmtfld.hxx>
#include <txtfld.hxx>
#include <svl/itempool.hxx>
#include <unotools/useroptions.hxx>
#include <svl/whiter.hxx>
@@ -186,7 +187,9 @@ void SwTextShell::ExecField(SfxRequest &rReq)
bAddSetExpressionFields ) )
{
rSh.ClearMark();
if ( dynamic_cast<SwInputField*>(rSh.GetCurField( true )) != nullptr )
if (!rSh.IsMultiSelection()
&& (nullptr != dynamic_cast<const SwTextInputField*>(
SwCursorShell::GetTextFieldAtCursor(rSh.GetCursor(), true))))
{
rSh.SttSelect();
rSh.SelectText(