1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <memory> 21 #include <bookmrk.hxx> 22 #include <IDocumentUndoRedo.hxx> 23 #include <IDocumentLinksAdministration.hxx> 24 #include <IDocumentState.hxx> 25 #include <doc.hxx> 26 #include <ndtxt.hxx> 27 #include <pam.hxx> 28 #include <swserv.hxx> 29 #include <sfx2/linkmgr.hxx> 30 #include <UndoBookmark.hxx> 31 #include <unobookmark.hxx> 32 #include <xmloff/odffields.hxx> 33 #include <libxml/xmlwriter.h> 34 #include <comphelper/random.hxx> 35 #include <comphelper/anytostring.hxx> 36 #include <sal/log.hxx> 37 #include <svl/zforlist.hxx> 38 #include <edtwin.hxx> 39 #include <DateFormFieldButton.hxx> 40 #include <DropDownFormFieldButton.hxx> 41 #include <DocumentContentOperationsManager.hxx> 42 43 using namespace ::sw::mark; 44 using namespace ::com::sun::star; 45 using namespace ::com::sun::star::uno; 46 47 namespace sw { namespace mark 48 { 49 FindFieldSep(IFieldmark const & rMark)50 SwPosition FindFieldSep(IFieldmark const& rMark) 51 { 52 SwPosition const& rStartPos(rMark.GetMarkStart()); 53 SwPosition const& rEndPos(rMark.GetMarkEnd()); 54 SwNodes const& rNodes(rStartPos.nNode.GetNodes()); 55 sal_uLong const nStartNode(rStartPos.nNode.GetIndex()); 56 sal_uLong const nEndNode(rEndPos.nNode.GetIndex()); 57 int nFields(0); 58 boost::optional<SwPosition> ret; 59 for (sal_uLong n = nEndNode; nStartNode <= n; --n) 60 { 61 SwNode *const pNode(rNodes[n]); 62 if (pNode->IsTextNode()) 63 { 64 SwTextNode & rTextNode(*pNode->GetTextNode()); 65 sal_Int32 const nStart(n == nStartNode 66 ? rStartPos.nContent.GetIndex() + 1 67 : 0); 68 sal_Int32 const nEnd(n == nEndNode 69 // subtract 1 to ignore the end char 70 ? rEndPos.nContent.GetIndex() - 1 71 : rTextNode.Len()); 72 for (sal_Int32 i = nEnd; nStart < i; --i) 73 { 74 const sal_Unicode c(rTextNode.GetText()[i - 1]); 75 switch (c) 76 { 77 case CH_TXT_ATR_FIELDSTART: 78 --nFields; 79 assert(0 <= nFields); 80 break; 81 case CH_TXT_ATR_FIELDEND: 82 ++nFields; 83 // fields in field result could happen by manual 84 // editing, although the field update deletes them 85 break; 86 case CH_TXT_ATR_FIELDSEP: 87 if (nFields == 0) 88 { 89 assert(!ret); // one per field 90 ret = SwPosition(rTextNode, i - 1); 91 #ifndef DBG_UTIL 92 return *ret; 93 #endif 94 } 95 break; 96 } 97 } 98 } 99 else if (pNode->IsEndNode()) 100 { 101 assert(nStartNode <= pNode->StartOfSectionIndex()); 102 // fieldmark cannot overlap node section 103 n = pNode->StartOfSectionIndex(); 104 } 105 else 106 { 107 assert(pNode->IsNoTextNode()); 108 } 109 } 110 assert(ret); // must have found it 111 return *ret; 112 } 113 } } // namespace sw::mark 114 115 namespace 116 { lcl_FixPosition(SwPosition & rPos)117 void lcl_FixPosition(SwPosition& rPos) 118 { 119 // make sure the position has 1) the proper node, and 2) a proper index 120 SwTextNode* pTextNode = rPos.nNode.GetNode().GetTextNode(); 121 if(pTextNode == nullptr && rPos.nContent.GetIndex() > 0) 122 { 123 SAL_INFO( 124 "sw.core", 125 "illegal position: " << rPos.nContent.GetIndex() 126 << " without proper TextNode"); 127 rPos.nContent.Assign(nullptr, 0); 128 } 129 else if(pTextNode != nullptr && rPos.nContent.GetIndex() > pTextNode->Len()) 130 { 131 SAL_INFO( 132 "sw.core", 133 "illegal position: " << rPos.nContent.GetIndex() 134 << " is beyond " << pTextNode->Len()); 135 rPos.nContent.Assign(pTextNode, pTextNode->Len()); 136 } 137 } 138 lcl_AssertFieldMarksSet(Fieldmark const * const pField,const sal_Unicode aStartMark,const sal_Unicode aEndMark)139 void lcl_AssertFieldMarksSet(Fieldmark const * const pField, 140 const sal_Unicode aStartMark, 141 const sal_Unicode aEndMark) 142 { 143 if (aEndMark != CH_TXT_ATR_FORMELEMENT) 144 { 145 SwPosition const& rStart(pField->GetMarkStart()); 146 assert(rStart.nNode.GetNode().GetTextNode()->GetText()[rStart.nContent.GetIndex()] == aStartMark); (void) rStart; (void) aStartMark; 147 SwPosition const sepPos(sw::mark::FindFieldSep(*pField)); 148 assert(sepPos.nNode.GetNode().GetTextNode()->GetText()[sepPos.nContent.GetIndex()] == CH_TXT_ATR_FIELDSEP); (void) sepPos; 149 } 150 SwPosition const& rEnd(pField->GetMarkEnd()); 151 assert(rEnd.nNode.GetNode().GetTextNode()->GetText()[rEnd.nContent.GetIndex() - 1] == aEndMark); (void) rEnd; 152 } 153 lcl_SetFieldMarks(Fieldmark * const pField,SwDoc * const io_pDoc,const sal_Unicode aStartMark,const sal_Unicode aEndMark,SwPosition const * const pSepPos)154 void lcl_SetFieldMarks(Fieldmark* const pField, 155 SwDoc* const io_pDoc, 156 const sal_Unicode aStartMark, 157 const sal_Unicode aEndMark, 158 SwPosition const*const pSepPos) 159 { 160 io_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr); 161 OUString startChar(aStartMark); 162 if (aEndMark != CH_TXT_ATR_FORMELEMENT 163 && pField->GetMarkStart() == pField->GetMarkEnd()) 164 { 165 // do only 1 InsertString call - to expand existing bookmarks at the 166 // position over the whole field instead of just aStartMark 167 startChar += OUStringChar(CH_TXT_ATR_FIELDSEP) + OUStringChar(aEndMark); 168 } 169 170 SwPosition start = pField->GetMarkStart(); 171 if (aEndMark != CH_TXT_ATR_FORMELEMENT) 172 { 173 SwPaM aStartPaM(start); 174 io_pDoc->getIDocumentContentOperations().InsertString(aStartPaM, startChar); 175 start.nContent -= startChar.getLength(); // restore, it was moved by InsertString 176 // do not manipulate via reference directly but call SetMarkStartPos 177 // which works even if start and end pos were the same 178 pField->SetMarkStartPos( start ); 179 SwPosition& rEnd = pField->GetMarkEnd(); // note: retrieve after 180 // setting start, because if start==end it can go stale, see SetMarkPos() 181 assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd)); 182 if (startChar.getLength() == 1) 183 { 184 *aStartPaM.GetPoint() = pSepPos ? *pSepPos : rEnd; 185 io_pDoc->getIDocumentContentOperations().InsertString(aStartPaM, OUString(CH_TXT_ATR_FIELDSEP)); 186 if (!pSepPos || rEnd < *pSepPos) 187 { // rEnd is not moved automatically if it's same as insert pos 188 ++rEnd.nContent; 189 } 190 } 191 assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd)); 192 } 193 else 194 { 195 assert(pSepPos == nullptr); 196 } 197 198 SwPosition& rEnd = pField->GetMarkEnd(); 199 if (aEndMark && startChar.getLength() == 1) 200 { 201 SwPaM aEndPaM(rEnd); 202 io_pDoc->getIDocumentContentOperations().InsertString(aEndPaM, OUString(aEndMark)); 203 ++rEnd.nContent; 204 } 205 lcl_AssertFieldMarksSet(pField, aStartMark, aEndMark); 206 207 io_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr); 208 }; 209 lcl_RemoveFieldMarks(Fieldmark const * const pField,SwDoc * const io_pDoc,const sal_Unicode aStartMark,const sal_Unicode aEndMark)210 void lcl_RemoveFieldMarks(Fieldmark const * const pField, 211 SwDoc* const io_pDoc, 212 const sal_Unicode aStartMark, 213 const sal_Unicode aEndMark) 214 { 215 io_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr); 216 217 const SwPosition& rStart = pField->GetMarkStart(); 218 SwTextNode const*const pStartTextNode = rStart.nNode.GetNode().GetTextNode(); 219 assert(pStartTextNode); 220 if (aEndMark != CH_TXT_ATR_FORMELEMENT) 221 { 222 (void) pStartTextNode; 223 // check this before start / end because of the +1 / -1 ... 224 SwPosition const sepPos(sw::mark::FindFieldSep(*pField)); 225 io_pDoc->GetDocumentContentOperationsManager().DeleteDummyChar(rStart, aStartMark); 226 io_pDoc->GetDocumentContentOperationsManager().DeleteDummyChar(sepPos, CH_TXT_ATR_FIELDSEP); 227 } 228 229 const SwPosition& rEnd = pField->GetMarkEnd(); 230 SwTextNode *const pEndTextNode = rEnd.nNode.GetNode().GetTextNode(); 231 assert(pEndTextNode); 232 const sal_Int32 nEndPos = (rEnd == rStart) 233 ? rEnd.nContent.GetIndex() 234 : rEnd.nContent.GetIndex() - 1; 235 assert(pEndTextNode->GetText()[nEndPos] == aEndMark); 236 SwPosition const aEnd(*pEndTextNode, nEndPos); 237 io_pDoc->GetDocumentContentOperationsManager().DeleteDummyChar(aEnd, aEndMark); 238 239 io_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr); 240 }; 241 } 242 243 namespace sw { namespace mark 244 { MarkBase(const SwPaM & aPaM,const OUString & rName)245 MarkBase::MarkBase(const SwPaM& aPaM, 246 const OUString& rName) 247 : m_pPos1(new SwPosition(*(aPaM.GetPoint()))) 248 , m_aName(rName) 249 { 250 m_pPos1->nContent.SetMark(this); 251 lcl_FixPosition(*m_pPos1); 252 if (aPaM.HasMark() && (*aPaM.GetMark() != *aPaM.GetPoint())) 253 { 254 MarkBase::SetOtherMarkPos(*(aPaM.GetMark())); 255 lcl_FixPosition(*m_pPos2); 256 } 257 } 258 259 // For fieldmarks, the CH_TXT_ATR_FIELDSTART and CH_TXT_ATR_FIELDEND 260 // themselves are part of the covered range. This is guaranteed by 261 // TextFieldmark::InitDoc/lcl_AssureFieldMarksSet. IsCoveringPosition(const SwPosition & rPos) const262 bool MarkBase::IsCoveringPosition(const SwPosition& rPos) const 263 { 264 return GetMarkStart() <= rPos && rPos < GetMarkEnd(); 265 } 266 SetMarkPos(const SwPosition & rNewPos)267 void MarkBase::SetMarkPos(const SwPosition& rNewPos) 268 { 269 std::make_unique<SwPosition>(rNewPos).swap(m_pPos1); 270 m_pPos1->nContent.SetMark(this); 271 } 272 SetOtherMarkPos(const SwPosition & rNewPos)273 void MarkBase::SetOtherMarkPos(const SwPosition& rNewPos) 274 { 275 std::make_unique<SwPosition>(rNewPos).swap(m_pPos2); 276 m_pPos2->nContent.SetMark(this); 277 } 278 ToString() const279 OUString MarkBase::ToString( ) const 280 { 281 return "Mark: ( Name, [ Node1, Index1 ] ): ( " + m_aName + ", [ " 282 + OUString::number( GetMarkPos().nNode.GetIndex( ) ) + ", " 283 + OUString::number( GetMarkPos().nContent.GetIndex( ) ) + " ] )"; 284 } 285 dumpAsXml(xmlTextWriterPtr pWriter) const286 void MarkBase::dumpAsXml(xmlTextWriterPtr pWriter) const 287 { 288 xmlTextWriterStartElement(pWriter, BAD_CAST("MarkBase")); 289 xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(m_aName.toUtf8().getStr())); 290 xmlTextWriterStartElement(pWriter, BAD_CAST("markPos")); 291 GetMarkPos().dumpAsXml(pWriter); 292 xmlTextWriterEndElement(pWriter); 293 if (IsExpanded()) 294 { 295 xmlTextWriterStartElement(pWriter, BAD_CAST("otherMarkPos")); 296 GetOtherMarkPos().dumpAsXml(pWriter); 297 xmlTextWriterEndElement(pWriter); 298 } 299 xmlTextWriterEndElement(pWriter); 300 } 301 ~MarkBase()302 MarkBase::~MarkBase() 303 { } 304 GenerateNewName(const OUString & rPrefix)305 OUString MarkBase::GenerateNewName(const OUString& rPrefix) 306 { 307 static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr); 308 309 if (bHack) 310 { 311 static sal_Int64 nIdCounter = SAL_CONST_INT64(6000000000); 312 return rPrefix + OUString::number(nIdCounter++); 313 } 314 else 315 { 316 static OUString sUniquePostfix; 317 static sal_Int32 nCount = SAL_MAX_INT32; 318 if(nCount == SAL_MAX_INT32) 319 { 320 unsigned int const n(comphelper::rng::uniform_uint_distribution(0, 321 std::numeric_limits<unsigned int>::max())); 322 sUniquePostfix = "_" + OUString::number(n); 323 nCount = 0; 324 } 325 // putting the counter in front of the random parts will speed up string comparisons 326 return rPrefix + OUString::number(nCount++) + sUniquePostfix; 327 } 328 } 329 Modify(const SfxPoolItem * pOld,const SfxPoolItem * pNew)330 void MarkBase::Modify( const SfxPoolItem *pOld, const SfxPoolItem *pNew ) 331 { 332 NotifyClients(pOld, pNew); 333 if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) 334 { // invalidate cached uno object 335 SetXBookmark(uno::Reference<text::XTextContent>(nullptr)); 336 } 337 } 338 339 // TODO: everything else uses MarkBase::GenerateNewName ? NavigatorReminder(const SwPaM & rPaM)340 NavigatorReminder::NavigatorReminder(const SwPaM& rPaM) 341 : MarkBase(rPaM, "__NavigatorReminder__") 342 { } 343 UnoMark(const SwPaM & aPaM)344 UnoMark::UnoMark(const SwPaM& aPaM) 345 : MarkBase(aPaM, MarkBase::GenerateNewName("__UnoMark__")) 346 { } 347 DdeBookmark(const SwPaM & aPaM)348 DdeBookmark::DdeBookmark(const SwPaM& aPaM) 349 : MarkBase(aPaM, MarkBase::GenerateNewName("__DdeLink__")) 350 { } 351 SetRefObject(SwServerObject * pObj)352 void DdeBookmark::SetRefObject(SwServerObject* pObj) 353 { 354 m_aRefObj = pObj; 355 } 356 DeregisterFromDoc(SwDoc * const pDoc)357 void DdeBookmark::DeregisterFromDoc(SwDoc* const pDoc) 358 { 359 if(m_aRefObj.is()) 360 pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer(m_aRefObj.get()); 361 } 362 ~DdeBookmark()363 DdeBookmark::~DdeBookmark() 364 { 365 if( m_aRefObj.is() ) 366 { 367 if(m_aRefObj->HasDataLinks()) 368 { 369 ::sfx2::SvLinkSource* p = m_aRefObj.get(); 370 p->SendDataChanged(); 371 } 372 m_aRefObj->SetNoServer(); 373 } 374 } 375 Bookmark(const SwPaM & aPaM,const vcl::KeyCode & rCode,const OUString & rName)376 Bookmark::Bookmark(const SwPaM& aPaM, 377 const vcl::KeyCode& rCode, 378 const OUString& rName) 379 : DdeBookmark(aPaM) 380 , ::sfx2::Metadatable() 381 , m_aCode(rCode) 382 , m_bHidden(false) 383 { 384 m_aName = rName; 385 } 386 InitDoc(SwDoc * const io_pDoc,sw::mark::InsertMode const,SwPosition const * const)387 void Bookmark::InitDoc(SwDoc* const io_pDoc, 388 sw::mark::InsertMode const, SwPosition const*const) 389 { 390 if (io_pDoc->GetIDocumentUndoRedo().DoesUndo()) 391 { 392 io_pDoc->GetIDocumentUndoRedo().AppendUndo( 393 std::make_unique<SwUndoInsBookmark>(*this)); 394 } 395 io_pDoc->getIDocumentState().SetModified(); 396 } 397 DeregisterFromDoc(SwDoc * const io_pDoc)398 void Bookmark::DeregisterFromDoc(SwDoc* const io_pDoc) 399 { 400 DdeBookmark::DeregisterFromDoc(io_pDoc); 401 402 if (io_pDoc->GetIDocumentUndoRedo().DoesUndo()) 403 { 404 io_pDoc->GetIDocumentUndoRedo().AppendUndo( 405 std::make_unique<SwUndoDeleteBookmark>(*this)); 406 } 407 io_pDoc->getIDocumentState().SetModified(); 408 } 409 GetRegistry()410 ::sfx2::IXmlIdRegistry& Bookmark::GetRegistry() 411 { 412 SwDoc *const pDoc( GetMarkPos().GetDoc() ); 413 assert(pDoc); 414 return pDoc->GetXmlIdRegistry(); 415 } 416 IsInClipboard() const417 bool Bookmark::IsInClipboard() const 418 { 419 SwDoc *const pDoc( GetMarkPos().GetDoc() ); 420 assert(pDoc); 421 return pDoc->IsClipBoard(); 422 } 423 IsInUndo() const424 bool Bookmark::IsInUndo() const 425 { 426 return false; 427 } 428 IsInContent() const429 bool Bookmark::IsInContent() const 430 { 431 SwDoc *const pDoc( GetMarkPos().GetDoc() ); 432 assert(pDoc); 433 return !pDoc->IsInHeaderFooter( GetMarkPos().nNode ); 434 } 435 MakeUnoObject()436 uno::Reference< rdf::XMetadatable > Bookmark::MakeUnoObject() 437 { 438 SwDoc *const pDoc( GetMarkPos().GetDoc() ); 439 assert(pDoc); 440 const uno::Reference< rdf::XMetadatable> xMeta( 441 SwXBookmark::CreateXBookmark(*pDoc, this), uno::UNO_QUERY); 442 return xMeta; 443 } 444 Fieldmark(const SwPaM & rPaM)445 Fieldmark::Fieldmark(const SwPaM& rPaM) 446 : MarkBase(rPaM, MarkBase::GenerateNewName("__Fieldmark__")) 447 { 448 if(!IsExpanded()) 449 SetOtherMarkPos(GetMarkPos()); 450 } 451 SetMarkStartPos(const SwPosition & rNewStartPos)452 void Fieldmark::SetMarkStartPos( const SwPosition& rNewStartPos ) 453 { 454 if ( GetMarkPos( ) <= GetOtherMarkPos( ) ) 455 return SetMarkPos( rNewStartPos ); 456 else 457 return SetOtherMarkPos( rNewStartPos ); 458 } 459 SetMarkEndPos(const SwPosition & rNewEndPos)460 void Fieldmark::SetMarkEndPos( const SwPosition& rNewEndPos ) 461 { 462 if ( GetMarkPos( ) <= GetOtherMarkPos( ) ) 463 return SetOtherMarkPos( rNewEndPos ); 464 else 465 return SetMarkPos( rNewEndPos ); 466 } 467 ToString() const468 OUString Fieldmark::ToString( ) const 469 { 470 return "Fieldmark: ( Name, Type, [ Nd1, Id1 ], [ Nd2, Id2 ] ): ( " + m_aName + ", " 471 + m_aFieldname + ", [ " + OUString::number( GetMarkPos().nNode.GetIndex( ) ) 472 + ", " + OUString::number( GetMarkPos( ).nContent.GetIndex( ) ) + " ], [" 473 + OUString::number( GetOtherMarkPos().nNode.GetIndex( ) ) + ", " 474 + OUString::number( GetOtherMarkPos( ).nContent.GetIndex( ) ) + " ] ) "; 475 } 476 Invalidate()477 void Fieldmark::Invalidate( ) 478 { 479 // TODO: Does exist a better solution to trigger a format of the 480 // fieldmark portion? If yes, please use it. 481 SwPaM aPaM( GetMarkPos(), GetOtherMarkPos() ); 482 aPaM.InvalidatePaM(); 483 } 484 dumpAsXml(xmlTextWriterPtr pWriter) const485 void Fieldmark::dumpAsXml(xmlTextWriterPtr pWriter) const 486 { 487 xmlTextWriterStartElement(pWriter, BAD_CAST("Fieldmark")); 488 xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldname"), BAD_CAST(m_aFieldname.toUtf8().getStr())); 489 xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldHelptext"), BAD_CAST(m_aFieldHelptext.toUtf8().getStr())); 490 MarkBase::dumpAsXml(pWriter); 491 xmlTextWriterStartElement(pWriter, BAD_CAST("parameters")); 492 for (auto& rParam : m_vParams) 493 { 494 xmlTextWriterStartElement(pWriter, BAD_CAST("parameter")); 495 xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(rParam.first.toUtf8().getStr())); 496 xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(comphelper::anyToString(rParam.second).toUtf8().getStr())); 497 xmlTextWriterEndElement(pWriter); 498 } 499 xmlTextWriterEndElement(pWriter); 500 xmlTextWriterEndElement(pWriter); 501 } 502 TextFieldmark(const SwPaM & rPaM,const OUString & rName)503 TextFieldmark::TextFieldmark(const SwPaM& rPaM, const OUString& rName) 504 : Fieldmark(rPaM) 505 { 506 if ( !rName.isEmpty() ) 507 m_aName = rName; 508 } 509 InitDoc(SwDoc * const io_pDoc,sw::mark::InsertMode const eMode,SwPosition const * const pSepPos)510 void TextFieldmark::InitDoc(SwDoc* const io_pDoc, 511 sw::mark::InsertMode const eMode, SwPosition const*const pSepPos) 512 { 513 if (eMode == sw::mark::InsertMode::New) 514 { 515 lcl_SetFieldMarks(this, io_pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos); 516 } 517 else 518 { 519 lcl_AssertFieldMarksSet(this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND); 520 } 521 } 522 ReleaseDoc(SwDoc * const pDoc)523 void TextFieldmark::ReleaseDoc(SwDoc* const pDoc) 524 { 525 IDocumentUndoRedo & rIDUR(pDoc->GetIDocumentUndoRedo()); 526 if (rIDUR.DoesUndo()) 527 { 528 rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this)); 529 } 530 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes 531 lcl_RemoveFieldMarks(this, pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND); 532 } 533 NonTextFieldmark(const SwPaM & rPaM)534 NonTextFieldmark::NonTextFieldmark(const SwPaM& rPaM) 535 : Fieldmark(rPaM) 536 { } 537 InitDoc(SwDoc * const io_pDoc,sw::mark::InsertMode const eMode,SwPosition const * const pSepPos)538 void NonTextFieldmark::InitDoc(SwDoc* const io_pDoc, 539 sw::mark::InsertMode const eMode, SwPosition const*const pSepPos) 540 { 541 assert(pSepPos == nullptr); 542 if (eMode == sw::mark::InsertMode::New) 543 { 544 lcl_SetFieldMarks(this, io_pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT, pSepPos); 545 546 // For some reason the end mark is moved from 1 by the Insert: 547 // we don't want this for checkboxes 548 SwPosition aNewEndPos = GetMarkEnd(); 549 aNewEndPos.nContent--; 550 SetMarkEndPos( aNewEndPos ); 551 } 552 else 553 { 554 lcl_AssertFieldMarksSet(this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT); 555 } 556 } 557 ReleaseDoc(SwDoc * const pDoc)558 void NonTextFieldmark::ReleaseDoc(SwDoc* const pDoc) 559 { 560 IDocumentUndoRedo & rIDUR(pDoc->GetIDocumentUndoRedo()); 561 if (rIDUR.DoesUndo()) 562 { 563 rIDUR.AppendUndo(std::make_unique<SwUndoDelNoTextFieldmark>(*this)); 564 } 565 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes 566 lcl_RemoveFieldMarks(this, pDoc, 567 CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT); 568 } 569 570 CheckboxFieldmark(const SwPaM & rPaM)571 CheckboxFieldmark::CheckboxFieldmark(const SwPaM& rPaM) 572 : NonTextFieldmark(rPaM) 573 { } 574 SetChecked(bool checked)575 void CheckboxFieldmark::SetChecked(bool checked) 576 { 577 if ( IsChecked() != checked ) 578 { 579 (*GetParameters())[OUString(ODF_FORMCHECKBOX_RESULT)] <<= checked; 580 // mark document as modified 581 SwDoc *const pDoc( GetMarkPos().GetDoc() ); 582 if ( pDoc ) 583 pDoc->getIDocumentState().SetModified(); 584 } 585 } 586 IsChecked() const587 bool CheckboxFieldmark::IsChecked() const 588 { 589 bool bResult = false; 590 parameter_map_t::const_iterator pResult = GetParameters()->find(OUString(ODF_FORMCHECKBOX_RESULT)); 591 if(pResult != GetParameters()->end()) 592 pResult->second >>= bResult; 593 return bResult; 594 } 595 FieldmarkWithDropDownButton(const SwPaM & rPaM)596 FieldmarkWithDropDownButton::FieldmarkWithDropDownButton(const SwPaM& rPaM) 597 : NonTextFieldmark(rPaM) 598 , m_pButton(nullptr) 599 { 600 } 601 ~FieldmarkWithDropDownButton()602 FieldmarkWithDropDownButton::~FieldmarkWithDropDownButton() 603 { 604 m_pButton.disposeAndClear(); 605 } 606 HideButton()607 void FieldmarkWithDropDownButton::HideButton() 608 { 609 if(m_pButton) 610 m_pButton->Show(false); 611 } 612 RemoveButton()613 void FieldmarkWithDropDownButton::RemoveButton() 614 { 615 if(m_pButton) 616 m_pButton.disposeAndClear(); 617 } 618 DropDownFieldmark(const SwPaM & rPaM)619 DropDownFieldmark::DropDownFieldmark(const SwPaM& rPaM) 620 : FieldmarkWithDropDownButton(rPaM) 621 { 622 } 623 ~DropDownFieldmark()624 DropDownFieldmark::~DropDownFieldmark() 625 { 626 } 627 ShowButton(SwEditWin * pEditWin)628 void DropDownFieldmark::ShowButton(SwEditWin* pEditWin) 629 { 630 if(pEditWin) 631 { 632 if(!m_pButton) 633 m_pButton = VclPtr<DropDownFormFieldButton>::Create(pEditWin, *this); 634 m_pButton->CalcPosAndSize(m_aPortionPaintArea); 635 m_pButton->Show(); 636 } 637 } 638 SetPortionPaintArea(const SwRect & rPortionPaintArea)639 void DropDownFieldmark::SetPortionPaintArea(const SwRect& rPortionPaintArea) 640 { 641 if(m_aPortionPaintArea == rPortionPaintArea && 642 m_pButton && m_pButton->IsVisible()) 643 return; 644 645 m_aPortionPaintArea = rPortionPaintArea; 646 if(m_pButton) 647 { 648 m_pButton->Show(); 649 m_pButton->CalcPosAndSize(m_aPortionPaintArea); 650 m_pButton->Invalidate(); 651 } 652 } 653 DateFieldmark(const SwPaM & rPaM)654 DateFieldmark::DateFieldmark(const SwPaM& rPaM) 655 : FieldmarkWithDropDownButton(rPaM) 656 , m_pNumberFormatter(nullptr) 657 , m_pDocumentContentOperationsManager(nullptr) 658 { 659 } 660 ~DateFieldmark()661 DateFieldmark::~DateFieldmark() 662 { 663 } 664 InitDoc(SwDoc * const io_pDoc,sw::mark::InsertMode eMode,SwPosition const * const pSepPos)665 void DateFieldmark::InitDoc(SwDoc* const io_pDoc, 666 sw::mark::InsertMode eMode, SwPosition const*const pSepPos) 667 { 668 m_pNumberFormatter = io_pDoc->GetNumberFormatter(); 669 m_pDocumentContentOperationsManager = &io_pDoc->GetDocumentContentOperationsManager(); 670 if (eMode == sw::mark::InsertMode::New) 671 { 672 lcl_SetFieldMarks(this, io_pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos); 673 } 674 else 675 { 676 lcl_AssertFieldMarksSet(this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND); 677 } 678 } 679 ReleaseDoc(SwDoc * const pDoc)680 void DateFieldmark::ReleaseDoc(SwDoc* const pDoc) 681 { 682 IDocumentUndoRedo & rIDUR(pDoc->GetIDocumentUndoRedo()); 683 if (rIDUR.DoesUndo()) 684 { 685 // TODO does this need a 3rd Undo class? 686 rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this)); 687 } 688 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes 689 lcl_RemoveFieldMarks(this, pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND); 690 } 691 ShowButton(SwEditWin * pEditWin)692 void DateFieldmark::ShowButton(SwEditWin* pEditWin) 693 { 694 if(pEditWin) 695 { 696 if(!m_pButton) 697 m_pButton = VclPtr<DateFormFieldButton>::Create(pEditWin, *this, m_pNumberFormatter); 698 SwRect aPaintArea(m_aPaintAreaStart.TopLeft(), m_aPaintAreaEnd.BottomRight()); 699 m_pButton->CalcPosAndSize(aPaintArea); 700 m_pButton->Show(); 701 } 702 } 703 SetPortionPaintAreaStart(const SwRect & rPortionPaintArea)704 void DateFieldmark::SetPortionPaintAreaStart(const SwRect& rPortionPaintArea) 705 { 706 if (rPortionPaintArea.IsEmpty()) 707 return; 708 709 m_aPaintAreaStart = rPortionPaintArea; 710 InvalidateCurrentDateParam(); 711 } 712 SetPortionPaintAreaEnd(const SwRect & rPortionPaintArea)713 void DateFieldmark::SetPortionPaintAreaEnd(const SwRect& rPortionPaintArea) 714 { 715 if (rPortionPaintArea.IsEmpty()) 716 return; 717 718 if(m_aPaintAreaEnd == rPortionPaintArea && 719 m_pButton && m_pButton->IsVisible()) 720 return; 721 722 m_aPaintAreaEnd = rPortionPaintArea; 723 if(m_pButton) 724 { 725 m_pButton->Show(); 726 SwRect aPaintArea(m_aPaintAreaStart.TopLeft(), m_aPaintAreaEnd.BottomRight()); 727 m_pButton->CalcPosAndSize(aPaintArea); 728 m_pButton->Invalidate(); 729 } 730 InvalidateCurrentDateParam(); 731 } 732 GetContent() const733 OUString DateFieldmark::GetContent() const 734 { 735 const SwTextNode* const pTextNode = GetMarkEnd().nNode.GetNode().GetTextNode(); 736 SwPosition const sepPos(sw::mark::FindFieldSep(*this)); 737 const sal_Int32 nStart(sepPos.nContent.GetIndex()); 738 const sal_Int32 nEnd (GetMarkEnd().nContent.GetIndex()); 739 740 OUString sContent; 741 if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() && 742 nEnd > nStart + 2) 743 sContent = pTextNode->GetText().copy(nStart + 1, nEnd - nStart - 2); 744 return sContent; 745 } 746 ReplaceContent(const OUString & sNewContent)747 void DateFieldmark::ReplaceContent(const OUString& sNewContent) 748 { 749 if(!m_pDocumentContentOperationsManager) 750 return; 751 752 const SwTextNode* const pTextNode = GetMarkEnd().nNode.GetNode().GetTextNode(); 753 SwPosition const sepPos(sw::mark::FindFieldSep(*this)); 754 const sal_Int32 nStart(sepPos.nContent.GetIndex()); 755 const sal_Int32 nEnd (GetMarkEnd().nContent.GetIndex()); 756 757 if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() && 758 nEnd > nStart + 2) 759 { 760 SwPaM aFieldPam(GetMarkStart().nNode, nStart + 1, 761 GetMarkStart().nNode, nEnd - 1); 762 m_pDocumentContentOperationsManager->ReplaceRange(aFieldPam, sNewContent, false); 763 } 764 else 765 { 766 SwPaM aFieldStartPam(GetMarkStart().nNode, nStart + 1); 767 m_pDocumentContentOperationsManager->InsertString(aFieldStartPam, sNewContent); 768 } 769 770 } 771 GetCurrentDate() const772 std::pair<bool, double> DateFieldmark::GetCurrentDate() const 773 { 774 // Check current date param first 775 std::pair<bool, double> aResult = ParseCurrentDateParam(); 776 if(aResult.first) 777 return aResult; 778 779 const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters(); 780 bool bFoundValidDate = false; 781 double dCurrentDate = 0; 782 OUString sDateFormat; 783 auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT); 784 if (pResult != pParameters->end()) 785 { 786 pResult->second >>= sDateFormat; 787 } 788 789 OUString sLang; 790 pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE); 791 if (pResult != pParameters->end()) 792 { 793 pResult->second >>= sLang; 794 } 795 796 // Get current content of the field 797 OUString sContent = GetContent(); 798 799 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType()); 800 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) 801 { 802 sal_Int32 nCheckPos = 0; 803 SvNumFormatType nType; 804 m_pNumberFormatter->PutEntry(sDateFormat, 805 nCheckPos, 806 nType, 807 nFormat, 808 LanguageTag(sLang).getLanguageType()); 809 } 810 811 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND) 812 { 813 bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sContent, nFormat, dCurrentDate); 814 } 815 return std::pair<bool, double>(bFoundValidDate, dCurrentDate); 816 } 817 SetCurrentDate(double fDate)818 void DateFieldmark::SetCurrentDate(double fDate) 819 { 820 // Replace current content with the selected date 821 ReplaceContent(GetDateInCurrentDateFormat(fDate)); 822 823 // Also save the current date in a standard format 824 sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters(); 825 (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= GetDateInStandardDateFormat(fDate); 826 } 827 GetDateInStandardDateFormat(double fDate) const828 OUString DateFieldmark::GetDateInStandardDateFormat(double fDate) const 829 { 830 OUString sCurrentDate; 831 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(ODF_FORMDATE_CURRENTDATE_FORMAT, ODF_FORMDATE_CURRENTDATE_LANGUAGE); 832 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) 833 { 834 sal_Int32 nCheckPos = 0; 835 SvNumFormatType nType; 836 OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT; 837 m_pNumberFormatter->PutEntry(sFormat, 838 nCheckPos, 839 nType, 840 nFormat, 841 ODF_FORMDATE_CURRENTDATE_LANGUAGE); 842 } 843 844 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND) 845 { 846 Color* pCol = nullptr; 847 m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentDate, &pCol, false); 848 } 849 return sCurrentDate; 850 } 851 ParseCurrentDateParam() const852 std::pair<bool, double> DateFieldmark::ParseCurrentDateParam() const 853 { 854 bool bFoundValidDate = false; 855 double dCurrentDate = 0; 856 857 const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters(); 858 auto pResult = pParameters->find(ODF_FORMDATE_CURRENTDATE); 859 OUString sCurrentDate; 860 if (pResult != pParameters->end()) 861 { 862 pResult->second >>= sCurrentDate; 863 } 864 if(!sCurrentDate.isEmpty()) 865 { 866 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(ODF_FORMDATE_CURRENTDATE_FORMAT, ODF_FORMDATE_CURRENTDATE_LANGUAGE); 867 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) 868 { 869 sal_Int32 nCheckPos = 0; 870 SvNumFormatType nType; 871 OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT; 872 m_pNumberFormatter->PutEntry(sFormat, 873 nCheckPos, 874 nType, 875 nFormat, 876 ODF_FORMDATE_CURRENTDATE_LANGUAGE); 877 } 878 879 if(nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND) 880 { 881 bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sCurrentDate, nFormat, dCurrentDate); 882 } 883 } 884 return std::pair<bool, double>(bFoundValidDate, dCurrentDate); 885 } 886 887 GetDateInCurrentDateFormat(double fDate) const888 OUString DateFieldmark::GetDateInCurrentDateFormat(double fDate) const 889 { 890 // Get current date format and language 891 OUString sDateFormat; 892 const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters(); 893 auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT); 894 if (pResult != pParameters->end()) 895 { 896 pResult->second >>= sDateFormat; 897 } 898 899 OUString sLang; 900 pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE); 901 if (pResult != pParameters->end()) 902 { 903 pResult->second >>= sLang; 904 } 905 906 // Fill the content with the specified format 907 OUString sCurrentContent; 908 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType()); 909 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) 910 { 911 sal_Int32 nCheckPos = 0; 912 SvNumFormatType nType; 913 OUString sFormat = sDateFormat; 914 m_pNumberFormatter->PutEntry(sFormat, 915 nCheckPos, 916 nType, 917 nFormat, 918 LanguageTag(sLang).getLanguageType()); 919 } 920 921 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND) 922 { 923 Color* pCol = nullptr; 924 m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentContent, &pCol, false); 925 } 926 return sCurrentContent; 927 } 928 InvalidateCurrentDateParam()929 void DateFieldmark::InvalidateCurrentDateParam() 930 { 931 std::pair<bool, double> aResult = ParseCurrentDateParam(); 932 if(!aResult.first) 933 return; 934 935 // Current date became invalid 936 if(GetDateInCurrentDateFormat(aResult.second) != GetContent()) 937 { 938 sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters(); 939 (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= OUString(); 940 } 941 } 942 }} 943 944 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 945