Kaydet (Commit) 742baabb authored tarafından Michael Stahl's avatar Michael Stahl Kaydeden (comit) Thorsten Behrens

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: 's avatarSamuel Mehrbrodt <Samuel.Mehrbrodt@cib.de>
Reviewed-on: https://gerrit.libreoffice.org/69414
Tested-by: Jenkins
Reviewed-by: 's avatarThorsten Behrens <Thorsten.Behrens@CIB.de>
üst a3881a66
......@@ -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 );
......
......@@ -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;
......
......@@ -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;
......
This diff is collapsed.
......@@ -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 );
......
......@@ -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:
{
......
......@@ -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,
......
......@@ -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);
}
......
......@@ -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();
......
......@@ -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.
......
......@@ -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:
......
......@@ -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(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment